一、题目描述
给定 n 个非负整数 height 表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
示例 1:
输入:
height = [0,1,0,2,1,0,1,3,2,1,2,1]
输出:
6
解释:
该高度图在下雨后可以接到 6 个单位的雨水。
示例 2:
输入:
height = [4,2,0,3,2,5]
输出:
9
二、问题分析
每个位置能接多少雨水,取决于:
min(左侧最高柱子, 右侧最高柱子) - 当前柱子高度
例如:
█
█ █
█ █ █ █
如果某一位置:
左边最高 = 4
右边最高 = 3
当前高度 = 1
那么该位置可以接的水为:
min(4,3) - 1 = 2
三、双指针解法(最优解)
为了高效计算,我们可以使用 双指针 + 动态维护左右最大值。
定义两个指针:
left = 0
right = n - 1
同时维护:
leftMax = 左侧最大高度
rightMax = 右侧最大高度
核心思想:
-
如果
height[left] < height[right]-
说明左侧是短板
-
用
leftMax计算水量
-
-
否则
-
右侧是短板
-
用
rightMax计算水量
-
因为水位始终取决于 较矮的一侧。
四、算法流程
-
初始化指针
left = 0
right = n - 1 -
记录左右最大高度
leftMax = 0
rightMax = 0 -
遍历数组
while(left < right)
-
判断左右高度关系
若:
height[left] < height[right]
更新左侧:
leftMax = max(leftMax, height[left])
water += leftMax - height[left]
否则更新右侧:
rightMax = max(rightMax, height[right])
water += rightMax - height[right]
- 移动指针
直到 left >= right。
五、C语言代码实现
int trap(int* height, int heightSize) {
int left = 0;
int right = heightSize - 1;
int leftMax = 0;
int rightMax = 0;
int water = 0;
while (left < right) {
if (height[left] < height[right]) {
if (height[left] >= leftMax)
leftMax = height[left];
else
water += leftMax - height[left];
left++;
} else {
if (height[right] >= rightMax)
rightMax = height[right];
else
water += rightMax - height[right];
right--;
}
}
return water;
}
六、复杂度分析
| 复杂度 | 说明 |
|---|---|
| 时间复杂度 | O(n) |
| 空间复杂度 | O(1) |
只遍历一次数组,并且没有使用额外数组空间。
七、示例分析
输入:
height = [0,1,0,2,1,0,1,3,2,1,2,1]
计算过程会累计得到:
1 + 1 + 2 + 1 + 1 = 6
最终结果:
6
八、总结
本题是 经典的双指针问题,核心思想是:
水位 = min(左侧最高柱子, 右侧最高柱子)
通过双指针不断向中间收缩,并动态维护 leftMax 和 rightMax,就可以在 O(n) 时间内计算出所有雨水量。
这种方法相比使用 leftMax[] 和 rightMax[] 数组的解法,空间复杂度从 O(n) 降到了 O(1),是面试中更推荐的写法。