- 基本的核心思想就是:当解决某个问题的时候,只关心最近一次的操作,并且在操作完成了之后,需要向前查找到更前一次的操作
- 每次处理只对栈顶元素进行处理
单调栈解决 Next Greater Number 一类问题
这里题目是给定一个数组,找出每一个元素之后比它大的数值或者索引号。最粗暴的方法就是遍历数组,但是这是一个复杂度的方法。
思想:
利用排队的思想,比如找第一个元素2的Next Greater Number,那么就是找那个比2要高的,不被2挡住的第一个元素,那就是4,以此类推。
利用栈,从数组的后面开始遍历,逐个入栈,实际出栈顺序却是正序。
vector<int> nextGreaterElement(vector<int>& nums) {
vector<int> ans(nums.size()); // 存放答案的数组
stack<int> s;
for (int i = nums.size() - 1; i >= 0; i--) { // 倒着往栈里放
while (!s.empty() && s.top() <= nums[i]) { // 判定个子高矮
s.pop(); // 矮个起开,反正也被挡着了。。。
}
ans[i] = s.empty() ? -1 : s.top(); // 这个元素身后的第一个高个
s.push(nums[i]); // 进队,接受之后的身高判定吧!
}
return ans;
}
如上面代码,for循环是从后遍历数组,while的判断是把当前的元素和第一个比当前元素大的中间元素全部弹出栈,因为它们的存在没有意义,最终只保留当前元素和它的Next Greater Number。
为什么会用到栈呢?
栈起到的作用
- 栈中保存了当前位置和其Next Greater Number以及更大的Next Greater Number,总之总会有一个最大的元素在里面,(在每一次查找Next Greater Number时都会清空当前位置和其Next Greater Number中间元素)
- 利用每次只能和栈顶元素进行操作的性质,不断根据判断,更新栈顶元素。(当前栈顶不满足就pop出来,让栈的下一个元素作为栈顶)
496. 下一个更大元素 I
739. 每日温度
503. 下一个更大元素 II
给一个字符串或一个数组要求对其进行分析
基本思想:
- 逐个迭代遍历字符串的每个字符或数组的元素,分别判断并做出相应操作
- 栈的作用是:
- 存储每一次成功判断下的信息。
- 每当遍历到某制定元素会进行相应的压栈或者出栈操作。
- 一般这里的判断会考虑到当前栈顶元素的值。
- 最终遍历结束后需要的信息会存放在栈中,可能会分层存放,也可能仅剩一层,或者栈为空,这具体情况具体分析,前两种情况需要对栈信息进行弹出读取,后一种情况只做结束判断条件,需要返回值会通过其他形式记录下来。
共同点:凡是遇到字符串或数组中的某一元素就会执行出栈操作
给一个字符串进行分析
这里的字符串分析,是要求对该字符串简化格式或者解读当前字符串含义,并最终输出分析后的正确格式。
20. 有效的括号
- 给了一个字符串,同样需要进行逐一遍历,通过判断是正或反括号分别进行压栈记录和出栈返回的的操作
- 栈的作用:
- 每当成功判断为正括号’(’’[’’{'就会进行压栈操作,那栈里面的信息只有正括号信息
- 每当遇到一个反括号’)’’]’’}’,都会与当前栈顶元素进行判断,如果是吻合的一对括号,那么会进行出栈操作,不吻合就返回错误。
- 最后栈为空,说明每一个正反括号都吻合,那么栈空就为结束条件。
- 给了一个冗余路径字符串,这字符串中包含多余的“/…” “/.” "/"符号,同样需要进行逐一遍历,凡是遇到’/‘就进入判断,通过判断在上一个’/‘到下一个’/'的字符串类型,执行相应的出栈和入栈操作,或者不执行
- 栈的作用:
- 每当遇到’/'符号就会进入判断,凡是到目前为止上一个’/‘到当前’/'不是等于"…"和“.”或者是空,分别进行出栈弹出上一层记录或者不执行任何操作进入下一轮循环,否则就会把当前记录的目录进行压栈。那么栈就是存储了正序的目前为止的绝对路径(出栈是逆序的),每一层就是每一级的目录名称。
- 最后遍历结束就退出,而不再是以栈为空作为唯一判断,但是如果栈为空退出循环那么说明当前输入的路径是错的
- 逐一遍历给定的字符串,凡是遇到数字,则会把数字记录下来,凡是 遇到’[’ 就会把记录的数字和上一层解码基础上记录的字符串压入各自栈中,凡是 遇到’]’ 会把存放数字和存放字符串的栈全部弹出,并进行解码操作
- 栈的作用:
- 两个栈,栈1在每一次在碰到’[‘时,压栈保存记录的数字,栈2在每一次遇到数字时,压栈保存上一层解码基础上,在遇到上一个’[‘之后记录的字符串。每当遇到’]'都各自弹出栈顶,并做解码。
- 这里判断并未考虑栈顶元素,但是会用栈顶元素进行操作。
- 直到遍历结束,此时的栈也会为空
给一个数组进行分析
- 逐一遍历数组元素,直到遇到算数符号就进行出栈操作,否则就是把每个数组元素执行进栈操作(在进栈前可能会进行相应处理)
- 栈的作用
- 存放的是每个数字元素,或者是上一层算术完成后的结果
- 凡是遇到算符运算符,就会正常弹出前两个栈元素,之后进行算术运算,并把结果压入栈中
- 直至遍历结束为止,此时栈只剩下一个元素,就是需要返回的结果
二叉树的遍历
二叉树遍历分为前序,中序,后序,层序遍历,区别主要是到达了当前节点后是否对当前节点进行访问和相关操作。
二叉树遍历也是逐一递进,直至访问到的节点为空,则会出栈弹出,回到上一层节点。
这也体现了栈的特点:在相当于存储了程序的入口断点地址,当执行子程序结束后则弹出栈回到上一层地址继续向下执行。犹如递归思想。只注重当前操作,当完成后返回上一层。
- 栈的作用:
- 在循环递进遍历节点过程中把还不需要访问的节点进行压栈保存
- 栈顶一般情况存储的是上一层节点
- 在当前遍历的节点为空的时候,就会弹出站定节点,相当于返回到上一层节点
- 直至栈为空,则遍历访问结束
前序遍历
前序遍历是先遍历每一个节点的左子树,后再访问其的右子树,但是每到达一个节点都会对当前节点进行访问和执行相关操作。
- 栈的作用:
- 经过循环遍历每一个节点左子树,并且每到一个节点就访问,同时把当前节点压入栈中
- 栈顶存放的是当前节点上一层节点,同时当前节点是上一层节点的左子树的第一个节点
- 直到当前节点为空,则说明其上层节点并没有左子树,那么出栈返回上一层节点,进入当前节点的右子树,返回到第一步
- 直至栈为空,访问结束
- 前序遍历顺序为:中 -> 左 -> 右
144.二叉树的前序遍历
中序遍历
中序遍历是先递进进入根节点左子树的最左节点,并访问它,之后再进入当前被访问节点的父节点的右子树,继续中序遍历,直至最终访问了根节点则认为访问结束。
- 栈的作用
- 不断遍历每个节点直至每个节点的左子树的最左节点,期间走过的节点都被压入栈中,但是不进行访问操作,这其实和前序一样的压栈情况。
- 栈顶存放的是当前走到的节点的上一层节点,和前序一样
- 知道当前节点为空,则同样说明其上层节点并没有左子树,那么出栈返回上一层节点,这时候会对当前节点进行访问,进入当前节点的右子树,返回到第一步
- 直至访问到根节点或者栈为空则说明访问结束
- 中序遍历顺序为: 左-> 中-> 右
- 和前序的区别
- 前序走过的每一个点都会访问,根节点是第一个访问的;中序只有是每个节点的左子树最左节点才会访问,根节点的左子树最左的节点会是第一个被访问的
- 前序在每一次压栈操作都会进行访问操作;中序在每一次出栈操作都会进行一次访问操作
后续遍历
后序遍历是先递进进入根节点左子树的最右节点并且访问,之后进入当前节点的父节点的右节点,再从新进行右子树遍历,直至右子树遍历结束,才会回到其父节点并且访问,访问顺序是:左->右->中
- 栈的作用
- 不断遍历每个节点直至每个节点的左子树的最左节点,期间走过的节点都被压入栈中,但是不进行访问操作,这其实和前序/中序一样的压栈情况。
- 栈顶存放的是当前走到的节点的上一层节点,和前序/中序一样
- 知道当前节点为空,则同样说明其上层节点并没有左子树,那么会使用top()回到上一层节点进入其右子树,继续返回1步骤,此时不出栈。
- 直到右子树也访问遍了,才会弹出当前父节点并且访问
- 直到栈为空与访问的节点为空则结束
- 访问顺序:左->右->中
来源:CSDN
作者:费费费费~
链接:https://blog.csdn.net/weixin_43205582/article/details/103624359