题目
给定一个表示柱子高度的数组,求下雨后最多能接多少雨水。
题目分析
这题难点不在公式,而在于“什么时候一个凹槽的面积已经可以确定”。
单调栈的核心思路是:当你遇到一个更高的右边界时,就可能把中间某个低谷的蓄水量结算出来。
方法一:直觉解法
最朴素的想法是:对每个位置分别找左侧最高和右侧最高,再算当前位置能装多少水。这能做,但需要额外数组。
方法二:优化思路
用单调递减栈存下标。当当前柱子更高时:
- 栈顶会作为“凹槽底部”被弹出
- 新栈顶是左边界
- 当前下标是右边界
这时一个局部凹槽的宽和高都可以算出来。
Go 实现
1func trap(height []int) int {
2 stack := make([]int, 0, len(height))
3 ans := 0
4
5 for i := 0; i < len(height); i++ {
6 for len(stack) > 0 && height[i] > height[stack[len(stack)-1]] {
7 bottom := stack[len(stack)-1]
8 stack = stack[:len(stack)-1]
9 if len(stack) == 0 {
10 break
11 }
12 left := stack[len(stack)-1]
13 width := i - left - 1
14 boundedHeight := min(height[left], height[i]) - height[bottom]
15 ans += width * boundedHeight
16 }
17 stack = append(stack, i)
18 }
19
20 return ans
21}
22
23func min(a, b int) int {
24 if a < b {
25 return a
26 }
27 return b
28}
复杂度分析
- 时间复杂度:
O(n) - 空间复杂度:
O(n)
易错点
- 弹出底部后,如果栈空了,说明左边界不存在,这一层不能结算
- 高度要用左右边界较小值减去底部高度
我的复盘
接雨水这题真正让我吃透了单调栈的“局部结算”思想。很多时候不是一次算完整答案,而是等条件成熟时把一部分答案结掉。