LeetCode选集:栈

71.简化路径

问题描述

题目描述

思路

  1. 以斜杠分隔字符串,斜杠间的内容有目录名、空、单点、双点四种可能
  2. 维护一个栈,顺序遍历斜杠间的内容,若为目录名则压入栈,若为双点且栈非空则弹出栈,其余两种情况不作处理

代码

string simplifyPath(string path)
	{
		vector<string>stack;
		string s;
		stringstream ss(path);
		while (getline(ss, s, '/'))
		{
			if (s == "..")
			{
				if (!stack.empty())
				{
					stack.pop_back();
				}
				continue;
			}
			if (s != ""&&s != ".")
			{
				stack.push_back(s);
			}
		}
		string result = "";
		for (auto &x : stack)
		{
			result += ("/" + x);
		}
		return result.empty() ? "/" : result;
	}

150.逆波兰表达式求值

问题描述

在这里插入图片描述

思路

维护一个栈,顺序遍历波兰表达式,若为数字则压入栈,若为运算符则计算栈顶两个数字的结果,弹出两数字并压入计算结果

代码

int evalRPN(vector<string> &tokens)
	{
		int num;
		deque<int>stack;
		for (const auto &c : tokens)
		{
			if (c == "+" || c == "-" || c == "*" || c == "/")
			{
				switch (c[0])
				{
				case '+':
					stack[stack.size() - 2] += stack.back();
					break;
				case '-':
					stack[stack.size() - 2] -= stack.back();
					break;
				case '*':
					stack[stack.size() - 2] *= stack.back();
					break;
				case '/':
					stack[stack.size() - 2] /= stack.back();
					break;
				}
				stack.pop_back();
			}
			else
			{
				stringstream ss(c);
				ss >> num;
				stack.push_back(num);
			}
		}
		return stack.front();
	}

173.二叉搜索树迭代器

问题描述

在这里插入图片描述

思路

  1. 维护一个栈,保存节点指针。初始化时,从根节点开始迭代,将所有左节点压入栈中
  2. 对所有压入栈中的节点来说,在其前压入的节点的值都较大,在其后压入的节点的值都较小
  3. 注意到压入栈中的节点可能拥有的右子树,右子树中的值大于其根节点的值,但相对于栈中其它节点的大小关系不变
  4. 综合上述三点,根据以下顺序遍历二叉树:二叉树=栈顶节点->该节点可能拥有的右子树->下一个栈顶节点

代码

class BSTIterator {
public:
    stack<TreeNode *> s;
    BSTIterator(TreeNode *root) 
    {
        PushTree(root);
    }
    bool hasNext() 
    {
        return !s.empty();
    }
    int next() 
    {
        TreeNode *node = s.top();
        s.pop();
        PushTree(node->right);
        return node->val;
    }
    void PushTree(TreeNode *root)
    {
        while(root != nullptr)
        {
            s.push(root);
            root = root->left;
        }
    }
};

402.移掉K位数字

问题描述

在这里插入图片描述

思路

  1. 维护一个栈,顺序遍历字符串,当栈非空、当前元素大于栈顶元素、尚未达到移出数量限制时,循环弹出栈顶元素,最后压入当前元素
  2. 移出元素的数量限制可能有富余,例如对于“12345”,上述步骤不会移出任何元素,所以要在最后从栈顶弹出剩余数量的元素
  3. 注意当前元素为’0’时,只要没有达到数量限制,栈中的元素可能全部被弹出。由于结果不能以‘0’开头,所以栈为空时不将‘0’压入栈

代码

string removeKdigits(string num, int k) 
	{
		string result = "";
		for (const auto &c : num)
		{
			while (!result.empty() && c < result.back() && k>0)
			{
				result.pop_back();
				k--;
			}

			if (result.empty() && c == '0')
			{
				continue;
			}
			result.push_back(c);
		}
		
		if (k >= result.size())
		{
			return "0";
		}
		return string(result.begin(), result.end() - k);
	}

496.下一个更大的元素Ⅰ

问题描述

在这里插入图片描述

思路

维护一个栈,顺序遍历nums2,先循环弹出小于当前元素的元素,之后压入当前元素。被弹出元素的下一个更大元素即为当前元素

代码

vector<int> nextGreaterElement(vector<int> &findNums, vector<int> &nums)
	{
		unordered_map<int, int>list;
		vector<int>stack;
		vector<int>result;

		for (auto beg = nums.begin(); beg != nums.end(); ++beg)
		{
			while (!stack.empty() && (*beg) > stack.back())
			{
				list[stack.back()] = *beg;
				stack.pop_back();
			}
			stack.push_back(*beg);
		}
		
		for (const auto &x : findNums)
		{
			result.push_back(list.find(x) == list.end() ? -1 : list[x]);
		}

		return result;
	}

503.下一个更大的元素Ⅱ

问题描述

在这里插入图片描述

思路

类似于496,但有两点不同:

  • 若单次遍历没有找到更大元素,可以再循环遍历一次,更大的元素可能出现在元素前面,所以将遍历改为两次即可
  • 该题只有一个输入,同时起到了496中findNums和nums的作用,意味着我们可以直接保存元素的索引而不必使用字典保存结果

代码

vector<int> nextGreaterElements(vector<int> &nums)
	{
		vector<int>result(nums.size(), -1);
		vector<pair<int,int>>value_Index;
		for (int n = 1; n <= 2 * nums.size(); n++)
		{
			int index = n > nums.size() ? (n - nums.size() - 1) : n - 1;
			while (!value_Index.empty() && nums[index] > value_Index.back().first)
			{
				result[value_Index.back().second] = nums[index];
				value_Index.pop_back();
			}
			if (result[index] == -1)
			{
				value_Index.push_back(pair<int, int>(nums[index], index));
			}
		}
		return result;
	}

636.函数独占时间

问题描述

在这里插入图片描述

思路

维护一个栈,顺序遍历输入,每次遍历可以得到三个值:id、“start”或“end”、time,若为start,则将time压入栈,若为“end”,则结合time和栈顶元素可以计算出函数id的独占时间dt。要注意求出dt后,栈中的其他元素即其它函数的开始时间都要加上dt。

代码

vector<int> exclusiveTime(int n, vector<string> &logs)
	{
		int id, time, pos1, pos2;
		string temp, flag;
		vector<int>stack;
		vector<int>result(n, 0);
		for (const auto &s : logs)
		{
			pos1 = s.find(":");
			pos2 = s.find(":", pos1 + 1);
			id = stoi(s.substr(0, pos1));
			time = stoi(s.substr(pos2 + 1));
			flag = s.substr(pos1 + 1, pos2 - pos1 - 1);

			if (flag == "end")
			{
				int dt = time - stack.back() + 1;
				result[id] += dt;
				stack.pop_back();
				for (auto &x : stack)
				{
					x += dt;
				}
			}
			else
			{
				stack.push_back(time);
			}
		}
		return result;
	}

735.行星碰撞

问题描述

在这里插入图片描述

思路

  1. 只有当左侧行星向右运动同时右侧行星向左运动才会发生碰撞
  2. 维护一个栈,遍历数组,左侧行星爆炸意味着弹出栈,右侧行星爆炸意味着不压入栈

代码

vector<int> asteroidCollision(vector<int> &asteroids)
	{
		vector<int>result;
		for (auto x : asteroids)
		{
			bool isExist = true;
			int weight_Right = abs(x);
			while (!result.empty() && result.back() > 0 && x < 0)
			{
				int &weight_Left = result.back();
				if (weight_Left < weight_Right)
				{
					result.pop_back();
				}
				else
				{
					if (weight_Left == weight_Right)
					{
						result.pop_back();
					}
					isExist = false;
					break;
				}
			}
			if (isExist)
			{
				result.push_back(x);
			}
		}
		return result;
	}

739.每日温度

问题描述

在这里插入图片描述

思路

该题与496、503类似,区别在于496、503求的是更大元素的值,而该题求的是元素与更大元素间的索引差,所以我们在栈中额外保存索引,在弹出元素时即可求出索引差

代码

vector<int> dailyTemperatures(vector<int> &T)
	{
		vector<pair<int, int>>stack;
		vector<int>result(T.size(), 0);
		for (int n = 1; n <= T.size(); n++)
		{
			while (!stack.empty() && T[n - 1] > stack.back().second)
			{
				result[stack.back().first - 1] = n - stack.back().first;
				stack.pop_back();
			}
			stack.push_back(pair<int, int>(n, T[n - 1]));
		}
		return result;
	}

901.股票价格跨度

问题描述

在这里插入图片描述

思路

对某一天的价格跨度,有两种情况:

  • 这一天的价格小于前一天的价格,则当天的价格跨度只能为1
  • 这一天的价格大于或等于前一天的价格,则当天的价格跨度可以包含前一天的价格跨度,因为前一天的价格跨度中的价格都是小于或等于前一天的价格的

代码

class StockSpanner 
{
public:
	StockSpanner() 
	{}

	int next(int price) 
	{
		int count = 1;
		while (!stack.empty() && price >= stack.back().first)
		{
			count += stack.back().second;
			stack.pop_back();
		}
		stack.push_back(pair<int, int>(price, count));
		return count;
	}
private:
	vector<pair<int, int>>stack;
};

921.使括号有效的最少添加

问题描述

在这里插入图片描述

思路

添加的括号有两种作用:

  • 补全左括号对应的右括号
  • 补全右括号对应的左括号

注意当右括号位于左括号左侧时,它们自身不能抵消,需要分别补齐

综上,维护一个栈,顺序遍历输入,当元素为’(‘时,压入栈,当元素为’)'时,若栈为空,说明该右括号位于所有左括号左侧,令结果+1,若栈非空,两括号抵消,弹出栈顶元素。遍历完成后,令结果+栈的长度,即需要补齐的左括号个数

代码

int minAddToMakeValid(string S)
	{
		vector<char>stack;
		int result = 0;
		for (const auto&c : S)
		{
			if (c == '(')
			{
				stack.push_back(c);
			}
			else
			{
				if (stack.empty())
				{
					result++;
				}
				else
				{
					stack.pop_back();
				}
			}
		}
		return result + stack.size();
	}

946.验证栈序列

问题描述

在这里插入图片描述

思路

当下一个要弹出的元素非空时,按以下顺序进行操作

  • 若当前栈顶元素等于下一个要弹出的元素,则弹出元素
  • 否则若下一个要压入的元素非空,则压入元素
  • 否则返回false

注意给出的压入序列和弹出序列长度相等,所以当下一个要弹出的元素为空时,下一个要压入的元素必为空,直接返回true

代码

bool validateStackSequences(vector<int> &pushed, vector<int> &popped)
	{
		vector<int>stack;
		auto beg_Pushed = pushed.begin(), beg_Poped = popped.begin();
		while (beg_Poped != popped.end())
		{
			if (!stack.empty() && stack.back() == *beg_Poped)
			{
				++beg_Poped;
				stack.pop_back();
			}
			else if (beg_Pushed == pushed.end())
			{
				return false;
			}
			else
			{
				stack.push_back(*beg_Pushed);
				++beg_Pushed;
			}
		}
		return true;
	}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值