写在前面:这个博客完全是学习了灵神之后,为了加深自己印象而写的,代码和思路并非原创。零零星星自己的理解和草图是自己的而已,90%的内容来自灵神的视频和题单。
单调栈用来解决什么问题?
解决:”一个数组,找元素x右边第一个比x大的数“ 这类问题。
1. 每日温度
单调栈模板题:739. 每日温度
思考过程:
代码:
func dailyTemperatures(temperatures []int) []int {
n := len(temperatures)
ans := make([]int, n) // n个元素,初始值为0
st := []int{}
for i := n - 1; i >= 0; i-- {
t := temperatures[i]
// 大于等于,而不是大于的原因是:
// 在相同的情况下,更靠近左边的元素应该被留下
// 而从右往左遍历,自然越后出现的越靠左边啦
for len(st) > 0 && t >= temperatures[st[len(st)-1]] {
st = st[:len(st)-1]
}
if len(st) > 0 {
ans[i] = st[len(st)-1] - i
}
st = append(st, i)
}
return ans
}
代码时间复杂度:O(N)
因为每个元素都只入栈一次。
理解深了每日温度后,可以举一反三下列题目:
2. 柱状图中的最大矩形
这是可以用单调栈解决的第二类问题,本质上还是求「右边第一个比x小和左边第一个比x小的数」。
2.1 题目描述 && 思路
问题描述:
思路:
矩形面积 = 长 * 宽
长肯定是每个柱子的高度中的一个,可以用反证法确定这一点。那么我们就可以枚举每个高度作为矩形的「长」。
现在就1个问题,矩形的宽怎么确定?
直接说答案:高度的左边右边第一个比它小的作为边界,中间的部分就是宽。
这样做你冥冥之中就感觉是对的,因为比x小的那个高度y,在枚举y的时候,就已经考虑过了。
行了,现在的问题变成如何求「左边右边第一个比它小的」,这不就是单调栈的拿手好戏了吗!
代码:
2.2 心得:这种题目的代码怎么想?
目标是找x右边「第一个」比x 「小」的,那么符合哪两个条件的应该放进来?
- 小
- 更靠左边。
所以来了一个元素,如果比栈顶的小或者相等,栈顶的元素应该滚蛋。
2.3 代码
func largestRectangleArea(heights []int) int {
// 找右边第一个比自己小的
// 栈应该从下往上越来越大
st := []int{}
n := len(heights)
right, left := make([]int, n), make([]int, n)
for i := n - 1; i >= 0; i-- {
h := heights[i]
for len(st) > 0 && h <= heights[st[len(st)-1]] {
st = st[:len(st)-1]
}
// 初始为n
right[i] = n
if len(st) > 0 {
right[i] = st[len(st)-1]
}
st = append(st, i)
}
st = []int{}
for i := 0; i < n; i++ {
h := heights[i]
for len(st) > 0 && h <= heights[st[len(st)-1]] {
st = st[:len(st)-1]
}
// 初始为-1
left[i] = -1
if len(st) > 0 {
left[i] = st[len(st)-1]
}
st = append(st, i)
}
// fmt.Println(left, right)
ans := 0
for i, h := range heights {
ans = max(ans, h * (right[i] - left[i] - 1))
}
return ans
}
2.4 举一反三
会了这个题,那你可NB了,可以直接做这三个题。
1793题:无非是在最后求面积的时候 增加一些if判断,注意你的left和right数组的值,跟题目描述的不完全一样。
85题: 最大矩形给了一个矩阵,别怕,对每一行做一个「柱状图中的最大矩形」。
221题:正方形?别慌,矩形的两条边找出来更短的那一条,然后平方即可。
3. 接雨水
这个题有双指针的解法,我觉得我就单调栈了。
思路:
当一个元素x来了,如果他比栈顶大,栈顶做为bottomH,弹出。
新的栈顶y。min(height[x], height[y]) - bottomH 作为水的高。
x - y - 1 作为水的宽。
代码:
func trap(height []int) int {
ans := 0
st := []int{}
for i, h := range height {
for len(st) > 0 && h >= height[st[len(st)-1]] {
bottomH := height[st[len(st)-1]]
st = st[:len(st)-1]
if len(st) == 0 {
break
}
left := st[len(st)-1]
dh := min(h, height[left]) - bottomH
ans += dh * (i - left - 1)
}
st = append(st, i)
}
return ans
}