遇到左括号入栈,遇到右括号出栈。需要注意的是:
- 在出栈时要判断当前栈是否为空
- 可能出现左括号的数目抑一直大于右括号的情况,此时栈即使在循环结束时也不为空
32.最长有效括号
我不会做!!!
这道题目有三种解法,分别是
- 动态规划
- 辅助栈
- 正向逆向结合
我在这里主要说一下如何使用栈来解决该问题。
听完闫总的讲解,我简单地复述一遍。
首先,有效括号要符合两个条件:
- 在一个有效括号字符串中,左括号的数目必须等于右括号的数目
- 在一个有效括号字符串中,任意字符串前缀中的左括号的数目都大于等于右括号的数目。
我们在给定的字符串挑选出右括号大于等于左括号的分界点(即包含该字符,恰好右括号的个数大于左括号),假设这样的分界点共有 n n n个。我们只需要在这 n n n个分界点中任意两个之间寻找最长的有效括号匹配即可。
为什么只需要在任意两个之间寻找最长的有效括号即可?
根据便是有效括号必须满足上述第二个条件,也就是说,有效括号不会跨过分界点。
42.接雨水
我不会做!!!
方法:单调栈或动态规划。
84.柱状图中的最大矩形
我不会做!!!
有两种暴力做法:
- 枚举左右区间,求解最小高度
- 枚举高度,向左右两侧求解左右区间,左右区间满足它的高度一定小于所枚举的高度(左右区间表示的区间是一个开区间)
考虑优化,可以使用单调栈来求解左右区间。
85.最大矩形
我不会做!!!
方法:
- 预处理
matrix[i][j]
左边连1
的个数(包括matrix[i][j]
),并将结果存放在left[i][j]
中- 枚举所有以
matrix[i][j]
为右下角的矩形,那么该矩形的最大宽度一定是left[i][j]
,left[i - 1][j]
⋯ \cdots ⋯left[k][j]
,0 <= k <= i
的最小值。优化
注意到第二步实际上就是在求解每一列(以该列的元素作为矩形的右下角)的矩形最大面积,因此我们可以按照84.柱状图中的最大矩形所使用的单调栈进行优化。
数据结构:栈
- 使用栈存储操作数,并从左到右遍历给定数组
- 遇到操作数字,入栈
- 遇到运算符,弹出栈中栈顶的两个操作数,对它们进行相应的操作,并将结果压入栈中。注意运算顺序,第一次从栈中弹出的操作数是第二个操作数,而第二次从栈中弹出的元素是第一个操作数。
整个逆波兰表达式遍历结束后,栈中只剩下一个操作数,该操作数即是逆波兰表达式的计算结果。
数据结构:栈
需要用到两个栈:
- 符号栈
op
:存放+,-,(
等运算符- 数据栈
num
:存放所有的数字从左到右遍历整个字符串,并做如下处理:
- 若遇到空格,跳过
- 遇到数字字符,继续遍历,知道遇到非数字字符为止,把一个连续的数字取出,放入
nums
中- 遇到左括号
(
,放入符号栈op
- 遇到右括号
)
,做如下操作:
- 若符号栈
op
栈顶元素不为)
,使用用现有的nums
和op
一直计算,直到符号栈op
栈顶元素不为)
- 弹出符号栈
op
栈顶元素- 遇到
+,-
:要放入符号栈op
中,在放入之前把栈内可以计算的数字都算掉,直到没有操作或栈顶元素为(
,计算结果放入num
中
进阶
请思考如下问题:如果表达式中包含*
,/
,%
等运算优先级更高的运算符,又该如何处理?
若表达式中包含比+
,-
运算优先级更高的运算符,在运算符加入符号栈之前,要判断栈顶运算符和当前运算符的优先级大小。若栈顶运算符的优先级大于等于当前运算符的优先级,则使用现有的num
和op
进行计算,并把计算结果放入数字栈,否则不对栈顶运算符做任何操作。最后把当前运算符加入符号栈。
一些细节:
- 由于第一个数可能是负数,为了避免边界判断,常用的小技巧是向
nums
添加一个0- 为了防止
()
内出现的第一个字符是运算符,在遇到左括号后,需要去除多余的空格并判断第一个有效字符是否是-
,如果是,则向nums
添加一个0。
栈:栈是一种后进先出的数据结构,即后加入栈中的元素会优先被弹出。
队列:队列是一种先进先出的数据结构,即先加入队列中的元素会优先被弹出。
用队列实现栈的关键在于修改翻转在队列中的顺序,即若加入数据的是
abc
,那么队列中数据的顺序应该是cba
。一个队列自然不可能实现上述功能,为此,我们可以借助一个辅助队列来实现。具体实现过程如下:
- 假设初始时主队列
q
的数据顺序是abcdefg
,辅助队列为空,待加入的数据是h
- 把
h
加到辅助队列当中- 把主队列中的元素移到辅助队列当中,此时辅助队列中的数据顺序是
habcdefg
- 把辅助队列中的数据移到主队列中
- 辅助队列重新为空,主队列中的数据顺序是
habcdefg
,满足栈后进先出的特性。
算法和思路:贪心 + 单调栈
具体而言,遍历字符串
s
,若s[i]
字典序大于当前栈顶元素的字典序,并且栈顶元素存在于字符串s
的第i
个元素的后面,那么删掉栈顶元素。重复上述过程直到栈中元素为空或栈顶元素字典序小于s[i]
。
还有一点需要注意,若当前元素
s[i]
在栈中已经出现过,那么就跳过本轮循环。
方法:深度优先搜索 或 栈
深度优先搜索
NestedInteger
中只能包含下面两部分之一:1. 数字 2. 一个列表,其中列表中的元素都是一个NestedInteger
。通过分析,不难得到NestedInteger
通过递归定义的,这启示我们可以使用深度优先搜索。
算法的具体步骤如下:
- 判断是否是数字,如果是,直接返回
- 递归处理字符串
s
- 遇到字符
[
,说明后面是一个NestedInteger。在遇到第一个[
时,什么也不做,遇到第二个[
,递归处理字符串s
- 遇到字符
]
,返回结果NestedInteger
- 遇到字符
,
,跳过不作处理 - 遇到数字
0-9
或-
,提取出数字num
,把num
加入到当前递归层下的NestedInteger
中
- 遇到字符
- 返回最后的结果
注意:
- 递归程序的接口是
NestedInteger dfs(string& s,int& idx)
- 为了使代码看起来更加清晰,可以把提取数字
num
的代码封装为一个函数
栈
可以用栈模拟上述思路。
每个[
的出现意味着一个嵌套类型的NestedInteger
的实例,每个]
的出现意味着一个嵌套类型的NestedInteger
的实例的结束。
遍历字符串s
,算法步骤如下:
- 遇到
[
,新建一个NestedInteger
实例,压入栈中 - 遇到
]
或,
,说明一个NestedInteger
实例结束,把该实例加入到栈顶元素中 - 遇到数字,把数字提取出来