zoukankan      html  css  js  c++  java
  • 【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)了。

  • 相关阅读:
    git的使用
    open():打开文件
    结合python+selenium使用AutoIt V3实现文件、图片上传
    Postman教程
    Python字符串切片
    python实现操作excel,数据写入excel的一行或者一列
    python打包exe
    spring boot基于DRUID数据源密码加密及数据源监控实现
    基于aop的日志记录方式实现
    Android中ContentProvider的简单使用
  • 原文地址:https://www.cnblogs.com/yanqiang/p/14099737.html
Copyright © 2011-2022 走看看