传送门:42. 接雨水 - 力扣(LeetCode)
🧠 题目描述
给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
示例 1:
输入: height = [0,1,0,2,1,0,1,3,2,1,2,1]
输出: 6
解释: 上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。
示例 2:
输入: height = [4,2,0,3,2,5]
输出: 9
提示:
n == height.length1 <= n <= 2 * 1040 <= height[i] <= 105
✅ 解法:双指针 + 左右最大值判断
通过这种方法可以避免了暴力法或动态规划中额外空间的开销。
🔍 变量说明
let res = 0; // 最终能接住的雨水总量
let l = 0; // 左指针
let r = height.length - 1; // 右指针
let pMax = 0; // 左边最高的柱子高度(从左往右扫描)
let sMax = 0; // 右边最高的柱子高度(从右往左扫描)
🔄 核心逻辑解析
while (l < r) {
pMax = Math.max(pMax, height[l]);
sMax = Math.max(sMax, height[r]);
if (pMax < sMax) {
res += pMax - height[l++];
} else {
res += sMax - height[r--];
}
}
1. 更新左右两边的最大高度
pMax = Math.max(pMax, height[l]):记录左边当前最大高度。sMax = Math.max(sMax, height[r]):记录右边当前最大高度。
2. 比较决定哪边可以积水
- 因为水的高度由较低的一侧决定(木桶原理),所以:
- 如果
pMax < sMax,那么当前位置l能接到的水量就是pMax - height[l]。 - 同理,如果
sMax <= pMax,那么当前位置r能接到的水量是sMax - height[r]。
- 如果
3. 移动指针
- 如果处理了
l,就l++; - 如果处理了
r,就r--。
📊 示例演示(一步步走)
以输入 [0,1,0,2,1,0,1,3,2,1,2,1] 为例:
| l | r | pMax | sMax | 操作 | res |
|---|---|---|---|---|---|
| 0 | 11 | 0 | 1 | pMax < sMax → 左:0-0=0 | 0 |
| 1 | 11 | 1 | 1 | 相等 → 右:1-1=0 | 0 |
| 1 | 10 | 1 | 2 | pMax < sMax → 左:1-1=0 | 0 |
| 2 | 10 | 1 | 2 | pMax < sMax → 左:1-0=1 | 1 |
| 3 | 10 | 2 | 2 | 相等 → 右:2-2=0 | 1 |
| 3 | 9 | 2 | 2 | 相等 → 右:2-1=1 | 2 |
| 3 | 8 | 2 | 2 | 相等 → 右:2-2=0 | 2 |
| 3 | 7 | 2 | 3 | pMax < sMax → 左:2-2=0 | 2 |
| 4 | 7 | 2 | 3 | pMax < sMax → 左:2-1=1 | 3 |
| 5 | 7 | 2 | 3 | pMax < sMax → 左:2-0=2 | 5 |
| 6 | 7 | 2 | 3 | pMax < sMax → 左:2-1=1 | 6 |
→ 循环结束(l=7, r=7),返回 6 ✅
最终结果:res = 6
⏱️ 时间 & 空间复杂度
-
时间复杂度:O(n)
每个元素最多被访问一次,l 和 r 向中间靠拢。 -
空间复杂度:O(1)
只用了几个变量,没有使用额外数组或栈。
解题代码
/**
* @param {number[]} height
* @return {number}
*/
var trap = function(height) {
let res = 0, l = 0, r = height.length - 1, pMax = 0, sMax = 0;
while (l < r) {
pMax = Math.max(pMax, height[l]);
sMax = Math.max(sMax, height[r]);
if (pMax < sMax) {
res += pMax - height[l++];
} else {
res += sMax - height[r--];
}
}
return res;
};
✅ 总结一句话:
这段代码通过两个指针从两端向中间遍历,利用“当前左右两侧最高柱子”的最小值减去当前柱子高度,来计算每一格能接住的水量,是一种非常高效的贪心算法。