本文为视频的原稿,访问 B 站看视频,通过视频更能领会要义哦!
使用场景 & 原理
解决的问题:在数组中,寻找下一个更大(小)的元素。
原理:
- 空间换时间,暴力解法,O(n^2) 可解,借助额外空间实现了降维,O(n) 可解。
- 求下一个更大元素时,构造单调递减的栈,遍历时一旦找到比栈顶大的元素,就可以把栈顶弹出,视为找到了栈顶元素的下一个更大元素。
示例
这一段视频里有详细解释单调栈的构建过程。
// 遍历下标: i
// arr: 73,74,75,71,69,72,76,73
// stack: 75,71,69,
// res: 74,75,76,72,72,76,?,?
代码模板
stack := make([]int, 0)
for i, v := range arr {
// 当前元素比栈顶大,视为找到下一个更大元素,就弹出栈顶,更新结果
for len(stack) > 0 && v > arr[stack[len(stack)-1]] {
topIndex := stack[len(stack)-1]
stack = stack[:len(stack)-1]
// 更新结果
}
// 当前元素比栈顶小,直接把下标入栈
stack = append(stack, i)
}
LeetCode 实战
以下实战题在我的主页都有讲过,欢迎观看:访问 B 站
739 - 每日温度
func dailyTemperatures(temperatures []int) []int {
ans := make([]int, len(temperatures))
stack := make([]int, 0)
for i, t := range temperatures {
// 当前元素比栈顶要大,更新结果,再去弹出栈顶元素
for len(stack) > 0 && t > temperatures[stack[len(stack)-1]] {
topIndex := stack[len(stack)-1]
ans[topIndex] = i - topIndex
stack = stack[:len(stack) - 1]
}
// 当前元素比栈顶要小,直接入栈
stack = append(stack, i)
}
return ans
}
// 遍历过程中,找到右侧第一个比自己大的元素的下标
// 暴力解法:O(N^2)
// 降维:空间换时间
// 数组,从左往右遍历,记录第一个比自己大的元素,找到后更新result,就可以把自己从数组中删去
// 栈来解决,单调栈
496 - 下一个更大元素 I
func nextGreaterElement(nums1 []int, nums2 []int) []int {
hashmap := map[int]int{}
res := make([]int, len(nums1))
stack := make([]int, 0)
for i, v := range nums2 {
// 找到比自己大的元素,就可以弹出
for len(stack) > 0 && v > nums2[stack[len(stack)-1]] {
hashmap[nums2[stack[len(stack)-1]]] = v
stack = stack[:len(stack)-1]
}
// 没有比自己大的,就放到栈中。把什么元素存到栈中,是看最终结果需要什么
stack = append(stack, i)
}
for i := range nums1 {
if val, ok := hashmap[nums1[i]]; ok {
res[i] = val
} else {
res[i] = -1
}
}
return res
}
503 - 下一个更大元素 II
func nextGreaterElements(nums []int) []int {
res := make([]int, len(nums))
for i := range res {
res[i] = -1
}
stack := make([]int, 0)
for i := 0; i < len(nums)*2; i++ {
index := i % len(nums)
// 找到比自己大的元素,就可以弹出
for len(stack) > 0 && nums[index] > nums[stack[len(stack)-1]] {
res[stack[len(stack)-1]] = nums[index]
stack = stack[:len(stack)-1]
}
// 没有比自己大的,就放到栈中。把什么元素存到栈中,是看最终结果需要什么
stack = append(stack, index)
}
return res
}
84 - 柱状图中最大的矩形
func largestRectangleArea(heights []int) int {
newHeights := make([]int, len(heights)+2)
for i := 0; i < len(heights); i++ {
newHeights[i+1] = heights[i]
}
res := 0
stack := make([]int, 0)
for i, height := range newHeights {
for len(stack) > 0 && height < newHeights[stack[len(stack)-1]] {
mid := stack[len(stack)-1]
stack = stack[:len(stack)-1]
if len(stack) > 0 {
h := newHeights[mid]
w := i - stack[len(stack)-1] - 1
res = max(res, h*w)
}
}
stack = append(stack, i)
}
return res
}
func max(x, y int) int {
if x > y {
return x
}
return y
}
// 找到比自身柱子低的左右柱子,面积=自身高度*左右柱子的距离
// 用单调递增的栈,左右柱子就方便找到
42 - 接雨水
func trap(height []int) int {
sum := 0
stack := make([]int, 0)
for i, h := range height {
// 遇到比栈顶元素高的,就弹出
for len(stack) > 0 && h > stack[len(stack)-1] {
mid := stack[len(stack)-1]
stack = stack[:len(stack)-1]
if len(stack) > 0 {
h := min(height[stack[len(stack)-1]], h) - height[mid]
w := i - stack[len(stack)-1] - 1
sum += h * w
}
}
// 比自己小,直接入栈
stack = append(stack, i)
}
return sum
}
func min(x, y int) int {
if x > y {
return y
}
return x
}
// 相对于自身,找左右比自己高的柱子
// 面积 = 矮柱子的高度 * 左右柱子的距离