【leetcode】栈、队列相关题目思路总结(更新中)

简单题

面试题03.02 栈的最小值

剑指offer30 包含min函数的栈

155. 最小栈

https://leetcode-cn.com/problems/min-stack/
https://leetcode-cn.com/problems/min-stack-lcci/
https://leetcode-cn.com/problems/bao-han-minhan-shu-de-zhan-lcof/

题目要求:
自定义栈,在满足pop和push之外,再提供min方法,可以获取栈中最小的元素。

解题思路:
额外维护一个最小值栈,记录栈中的最小值。
因为栈的最小值就在栈顶,所以每次新加入元素时,只需要与栈顶比较,即可知道新的最小值是多少。
如果新元素更小,则新元素复制一份入最小值栈,如果不是,则将栈顶再复制一份入栈。

复杂度分析:
push、pop、min都是O(1)的时间复杂度。
空间复杂度为O(n)。

剑指offer09 用两个栈实现队列

面试题 03.04. 化栈为队

232. 用栈实现队列

https://leetcode-cn.com/problems/yong-liang-ge-zhan-shi-xian-dui-lie-lcof/
https://leetcode-cn.com/problems/implement-queue-using-stacks-lcci/
https://leetcode-cn.com/problems/implement-queue-using-stacks/

题目要求:
用两个栈实现一个队列。

解题思路:
用一个栈in_stack专门进行push,用一个栈out_stack来pop。
如果out_stack为空,则将in_stack的倒入out_stack。

queue: 1(front), 2, 3, 4, 5(back) 
in_stack: 4, 5(top), out_stack: 3, 2, 1(top)

复杂度分析:
每个元素最多被插入和弹出in_stack out_stack各一次,所以时间复杂度O(1)。
空间复杂度为O(n)。

225 用队列实现栈

https://leetcode-cn.com/problems/implement-stack-using-queues/

题目要求:
用两个队列实现栈。

解题思路:
pop元素直接可以从队列顶删去,push元素需要一个临时栈,先将新元素进队,然后再将其他元素进队。之后再全部倒回原队列,或者互换两个队列。
用一个队列也是可以的,就是将n-1个元素先出队再入队。

queue: 5 4 3 2 1
queue_temp: 6
stack: 1 2 3 4 5 + 6

复杂度分析:
入栈操作 O(n),其余操作都是 O(1)。

20 有效的括号

https://leetcode-cn.com/problems/valid-parentheses/solution/you-xiao-de-gua-hao-by-leetcode-solution/

题目要求:
判断大括号、中括号、小括号的顺序是否合法。

解题思路:
用栈来解决即可。对于配对问题,可以直接if-else,也可以用unordered_map。

复杂度分析:
时间复杂度:O(n),和序列等长。
空间复杂度:O(n+∣Σ∣) ,序列+字典长。

844 比较含退格的字符串

https://leetcode-cn.com/problems/backspace-string-compare/

题目要求:
字符串中包括退格符#,判断两个字符串是否相等。

解题思路:
方法一,用栈,遇到普通字符入栈,遇到#则出栈。时间和空间复杂度均为O(M+N),M和N是两个字符串的长度。
方法二,双指针,定义两个指针从两个字符串的末尾开始向前比较。时间复杂度为O(M+N),空间复杂度为O(1)。

1047 删除字符串中的所有相邻重复项

https://leetcode-cn.com/problems/remove-all-adjacent-duplicates-in-string/

题目要求:
连连看,不断从字符串中删除成对相同的元素。

解题思路:
方法一,直接用字符串的替换方法,将aa,bb,...,zz全部替换为空字符串。因为需要反复多次,所以时间复杂度O(n^2), 空间复杂度O(n)。
方法二,用栈。时间复杂度和空间复杂度均为O(n)。

346. 数据流中的移动平均值

https://leetcode-cn.com/problems/moving-average-from-data-stream/

题目要求:
计算一个移动窗口内的数据的平均值,窗口大小固定。

解题思路:
方法一,每次将新元素入队列后,遍历计算窗口内的数据的平均值,时间复杂度为O(n),空间复杂度为O(M), M是序列长。
方法二,维持队列的大小,每次入队列后,如果超过窗口大小,则出队列一个,并记录每次的平均值,通过加减计算得到新的平均值。时间复杂度O(1), 空间复杂度O(N)。
方法三,采用基于数组的循环队列,时间空间复杂度同上,优点是不用显式地执行出队操作。

剑指 Offer 59 - I. 滑动窗口的最大值 (好题,思路经典)

https://leetcode-cn.com/problems/hua-dong-chuang-kou-de-zui-da-zhi-lcof/

题目要求:
窗口大小固定,求一个移动窗口里的最大值。

解题思路:
采用deque实现单调队列,维护一个非严格递减的队列。要很好地理解这一思想,需要用高度遮挡做类比。
这个队列的队首元素是区间里的最大的元素。当数据流出队的值和单调队列的队首值相等,则单调队列执行出队,否则,不用操作。
当数据流入队的值比单调队列队尾的值大时,丢弃该队尾值,如果还有小的,则继续丢弃。
时间复杂度O(n), 空间复杂度O(k), n为数据流队列长度,k为窗口大小。

1441. 用栈操作构建数组

https://leetcode-cn.com/problems/build-an-array-with-stack-operations/

题目要求:
从1到n按顺序遍历数,然后入栈或者出栈,得到操作序列。

解题思路:
如果数组中有的数,则是执行了一次入栈,如果没有,则执行了入栈+出栈。

933. 最近的请求次数

https://leetcode-cn.com/problems/number-of-recent-calls/solution/zui-jin-de-qing-qiu-ci-shu-by-leetcode/

题目要求:
不断发送ping请求,返回最近3000毫秒的请求次数。

解题思路:
每次入队的时候,循环判断队首是否在3000毫秒以内,如果不是则出队,最后返回队长。

682. 棒球比赛

https://leetcode-cn.com/problems/baseball-game/

题目要求:
一个特殊的积分规则。

解题思路:
使用栈实现,其中上次比赛和上上次比赛的积分求和,可以先出栈一次,再入栈一次。
时间空间复杂度都是O(N)。

496. 下一个更大元素 I

https://leetcode-cn.com/problems/next-greater-element-i/

题目要求:
求数组后面比数组当前值更大的下一个值。

解题思路:
这道题和滑动窗口最大值类似,也是高度遮挡的概念。
维护一个非严格递减栈,从右向左遍历。
或者一个非严格递增栈,从左向右遍历。

1021. 删除最外层的括号

https://leetcode-cn.com/problems/remove-outermost-parentheses/

题目要求:
找原语,并去掉原语外侧的括号,再合并。

解题思路:
判断括号的层级深度,如果是1级的括号,则忽略,2级及更深层的则直接复制给结果,这道题没有必要用栈。

1544. 整理字符串

https://leetcode-cn.com/problems/make-the-string-great/

题目要求:
将成对的大小写字母删掉。

解题思路:
不需要用栈,直接扫描字符串,如果有成对的,则忽略,将后面的往前复制(移动)。
可以实现就地算法。

1598. 文件夹操作日志搜集器

https://leetcode-cn.com/problems/crawler-log-folder/

题目要求:
给一些操作序列,判断最终返回主目录的步数。

解题思路:
可以使用栈,也可以不使用而直接用if-else计数。

中等题

402. 移掉K位数字 (好题,考验细节)

https://leetcode-cn.com/problems/remove-k-digits/

题目要求:
将一个数字中的k位删掉,最后得到的数要最小。

解题思路:
最终得到的数,应该是数字高位尽可能小,因此:
方法一,从左至右,设定窗口大小k+1,然后挑选最小的数字保留,该数左边的删去(最多删k个)。
方法二,使用非严格递增的单调栈,遍历,如果后面遇到的数比前边的小,则逐个删掉前边的数,保持序列递增性,用栈实现。
要处理的特殊情况:
k个数字要保证删够。
如果最终的数字序列存在前导零,我们要删去前导零。
如果最终数字序列为空,我们应该返回 0。

739. 每日温度

https://leetcode-cn.com/problems/daily-temperatures/

题目要求:
给一个温度序列,返回到下一个温度更高的时间的距离。

解题思路:
这道题和下一个更大元素题类似,不过是增加了距离计算。
多设置一个stack同步下标,即可得到距离。
另外一种方法是,利用输入vector按index取值,以及用输出vector元素自增,只用一个保存下标的stack即可。

1190. 反转每对括号间的子串(好题,思路清奇)

https://leetcode-cn.com/problems/reverse-substrings-between-each-pair-of-parentheses/

题目要求:
将一个字符串里括号中的内容逐级反转。

解题思路:
方法一,可以用栈判断括号的层级,然后写一个函数专门反转字符串,时间复杂度为O(n^2)。
方法二,一个很神奇的方法(黑洞法),先遍历一遍确定括号位置存到map里,然后开始遍历,遇到括号则跳到对应的括号处,并更改方向,前进一步。边界条件是,最终会跳出序列,只要判断位置合法。
方法二的时间复杂度为O(n),思路很不错,很难想到。

456. 132模式

https://leetcode-cn.com/problems/132-pattern/

题目要求:
从序列中找到符合132模式的数字,即3元组,左边<右边<中间。

解题思路:
首先从左到右遍历一遍得到最小值栈,这样下一步从右往左遍历的时候,可以知道左边有更小的值。
然后从右往左遍历,将当前值按条件依次入栈,同时最小值栈依次出栈。
条件:如果当前值小于栈顶则入栈,等于则跳过。大于栈顶则找到了比右边大的数。
不过右边的数不一定是更大的,所以在不大于当前值的情况下,逐个出栈,保证取到较大的数(当前值应当也在遍历时插入其中,后续会使用到)。
此时再判断如果这两个数比最小值栈栈顶大,则132三元组就找到了。

856. 括号的分数

https://leetcode-cn.com/problems/score-of-parentheses/

题目要求:
给定一个平衡括号字符串 S,按下述规则计算该字符串的分数:
() 得 1 分。
AB 得 A + B 分,其中 A 和 B 是平衡括号字符串。
(A) 得 2 * A 分,其中 A 是平衡括号字符串。

解题思路:
方法一,先遍历一遍,找到括号对,索引存到map里。然后将这一级的括号结果相加,下一级的则递归。时间空间复杂度均为O(n)。
方法二,维护一个栈,动态存储结果,遇到左括号则将0入栈,遇到右括号,则结果加到栈顶,同一级的直接相加,返回上级则乘2加到上级,最后得到总的结果,时间空间复杂度均为O(n)。
方法三,数学本质,找到层级最高的括号,则这个括号的值为2^n,最后把这些最高层级的括号相加既可,时间复杂度O(n),空间O(1)。

1209. 删除字符串中的所有相邻重复项 II

https://leetcode-cn.com/problems/remove-all-adjacent-duplicates-in-string-ii/

题目要求:
给你一个字符串 s,「k 倍重复项删除操作」将会从 s 中选择 k 个相邻且相等的字母,并删除它们,使被删去的字符串的左侧和右侧连在一起。
你需要对 s 重复进行无限次这样的删除操作,直到无法继续为止。
在执行完所有删除操作后,返回最终得到的字符串。

解题思路:
这道题方法较多,基本上都是要用到栈,空间时间复杂度最优为O(n)。
类似快慢双指针法,用两个双端队列,一个存字符,一个存计数,扫描字符串,如果字符相同,则累加计数,计数达到3,则删除。最后从队列前端倒出来,组成新字符串。

394. 字符串解码

https://leetcode-cn.com/problems/decode-string/

题目要求:
给定一个经过编码的字符串,返回它解码后的字符串。
编码规则为: k[encoded_string],表示其中方括号内部的 encoded_string 正好重复 k 次。注意 k 保证为正整数。
你可以认为输入字符串总是有效的;输入字符串中没有额外的空格,且输入的方括号总是符合格式要求的。
此外,你可以认为原始数据不包含数字,所有的数字只表示重复的次数 k ,例如不会出现像 3a 或 2[4] 的输入。

解题思路:
因为会有嵌套括号存在的情况,所以需要从最深层逐层向上处理,因此要保存好各层的变量。
使用一个栈保存该级待重复次数,使用一个栈保存该级需要重复的字母数,使用一个双端队列作为输入输出的数据流。
需要考虑的细节:
重复次数数字可能大于个位数,所以需要对数字进行拼接。
深层的重复后,需要更新上一级的字母数。
因为可能一开始只有字母,因此这个也要算一级(重复次数为1)。

946. 验证栈序列

https://leetcode-cn.com/problems/validate-stack-sequences/

题目要求:
判断list2是不是list1的合法出栈序列。

解题思路:
贪心法,每次先从list1中取一个数入栈,然后循环判断栈顶和list2头部的元素是否相等,相等则出栈。最后判断栈是否已清空,未清空则为false,已清空则为true。

71. 简化路径

https://leetcode-cn.com/problems/simplify-path/

题目要求:
给一个linux的路径,然后简化一下,其中有.以及..还有多个/。

解题思路:
压栈既可,注意对于几个特殊符号的处理逻辑的严密性,简单题。

144. 二叉树的前序遍历(后续需要学习Morris方法)

https://leetcode-cn.com/problems/binary-tree-preorder-traversal/

题目要求:
前序遍历。

解题思路:
用栈实现,判断是否为空,非空则先将父节点入栈。
然后循环执行,栈顶出栈,结果保存,将右子树入栈,再将左子树入栈,直到栈内为空。这种方法的时空复杂度均为O(n)。
此外还有一种Morris方法,充分利用空闲指针,空间复杂度为O(1)。值得学习,后面几道题都可以用。

94. 二叉树的中序遍历

https://leetcode-cn.com/problems/binary-tree-inorder-traversal/

题目要求:
中序遍历。

解题思路:
用栈实现,判断是否为空,非空则将父节点入栈。
然后循环执行,栈顶出栈,判断如果没有左右子树,则将值保存,否则,将右子树入栈,在将父节点减掉子树后入栈,将左子树入栈。

145. 二叉树的后序遍历

https://leetcode-cn.com/problems/binary-tree-postorder-traversal/

题目要求:
后序遍历。

解题思路:
与上面两题类似,不再赘述。

582. 杀死进程

https://leetcode-cn.com/problems/kill-process/

题目要求:
进程的父子关系类似于树,一个父进程会有几个子进程,求一个进程被杀死后,哪些进程一起死掉了。

解题思路:
方法一,可以直接构造树,然后遍历要杀死的子树。时间空间复杂度为O(n)。
方法二,用哈希表保存父子关系,然后用BFS或者DFS进行遍历。

面试题 17.09. 第 k 个数

https://leetcode-cn.com/problems/get-kth-magic-number-lcci/

题目要求:
一个数因式分解只能包括3,5,7这三种,1也算。序列1,3,5,7,9,15,求第k个。

解题思路:
这道题的解题思路非常巧妙。
先弄个vector存结果,然后设置三个指针,表示3,5,7*的vector里的哪个位置的元素。
找到值最小的那个,并将对应相等的指针(可能不止一个)向后移动。

622. 设计循环队列

https://leetcode-cn.com/problems/design-circular-queue/

题目要求:
基于定长数组实现一个循环队列。

解题思路:
主要采用两个指针实现,一个头指针,一个尾指针。区间为左闭右开。各种条件判断要注意不要出错。

641. 设计循环双端队列

https://leetcode-cn.com/problems/design-circular-deque/

题目要求:
基于定长数组实现一个双端队列。

解题思路:
和上题类似,也是用两个指针,不同点就是插入和删除多了一个方向。

173. 二叉搜索树迭代器

https://leetcode-cn.com/problems/binary-search-tree-iterator/

题目要求:
根据二叉搜索树构建,可以返回next()和hasNext(),要求O(1)时间复杂度,O(k)空间。

解题思路:
因为二叉搜索树是可以通过中序遍历获得最小值的,所以在初始化方法中先做树的中序遍历,转存到queue中。

921. 使括号有效的最少添加(TODO:理解一下第二种方法)

https://leetcode-cn.com/problems/minimum-add-to-make-parentheses-valid/

题目要求:
使括号序列有效,最少需要添加的括号数目。

解题思路:
用栈存,括号成对的删去,不成对的就是最后的结果。
此外,还有一个方法,计算前缀子数组的平衡度,根据正负加上相应左右括号。

1381. 设计一个支持增量操作的栈

https://leetcode-cn.com/problems/design-a-stack-with-increment-operation/

题目要求:
设计固定长度的栈,可以支持一定限度的push,以及对栈底k个元素值的增加。

解题思路:
直接用vector来模拟。简单题。
比较骚气的一个方法是,可以把要增加的先存起来,在出栈的时候计算,这样整个的时间复杂度就变成O(1)了。

posted @ 2020-12-10 01:53  Yanqiang  阅读(541)  评论(0编辑  收藏  举报