题目描述
给定 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
思路
要计算能够接多少雨水,我们需要找到每个柱子左右两边的最高柱子,然后计算在该柱子位置能够接多少水。
- 双指针(双边遍历):
- 左指针 i 从数组起始端向右移动。
- 右指针 j 从数组末端向左移动。
- 通过比较左右两侧的最大柱子高度,决定移动哪一端的指针,以优化计算过程。
- 左右最大柱子 leftMax 和 rightMax:
- leftMax 记录当前位置左侧(包括当前位置)的最高柱子高度。
- rightMax 记录当前位置右侧(包括当前位置)的最高柱子高度。
- 这些值用于确定当前位置能够接多少雨水。
- 雨水计算:
- 对于每个位置,接的雨水量取决于左右两侧的最低最大柱子高度减去当前位置柱子的高度。
- 如果结果为负,则表示当前位置无法接雨水,取
0。
- 移动指针的策略:
- 如果 leftMax < rightMax:
- 因为 leftMax 是限制因素,计算并累加当前位置接的雨水量,然后移动左指针。
- 否则:
- 因为 rightMax是限制因素,计算并累加当前位置接的雨水量,然后移动右指针。
- 这种策略保证总是向着可能增加接水量的一端前进。
- 如果 leftMax < rightMax:
复杂度分析
- 时间复杂度:O(N)
- 仅需一次遍历整个数组,双指针各自移动,共进行 N 次操作。
- 空间复杂度:O(1)
- 只使用了常数级别的额外空间,几个变量用于记录指针位置和最大高度。
code
var trap = function (height) {
let i = 0,
j = height.length - 1;
let leftMax = 0,
rightMax = 0;
let res = 0;
while (i <= j) {
leftMax = Math.max(leftMax, height[i]);
rightMax = Math.max(rightMax, height[j]);
if (leftMax < rightMax) {
res += Math.max(0, leftMax - height[i]);
i++;
} else {
res += Math.max(0, rightMax - height[j]);
j--;
}
}
return res;
};
let height = [0, 1, 0, 2, 1, 0, 1, 3, 2, 1, 2, 1];
console.log(trap(height));
步骤
| 步骤 | i | j | height[i] | height[j] | leftMax | rightMax | 操作 | [res](vscode-file://vscode-app/d:/Microsoft VS Code/resources/app/out/vs/code/electron-sandbox/workbench/workbench.html) |
|---|---|---|---|---|---|---|---|---|
| 1 | 0 | 11 | 0 | 1 | 0 | 1 | 左 < 右 => res += 0, 移动 i到 1 | 0 |
| 2 | 1 | 11 | 1 | 1 | 1 | 1 | 左 >= 右 => res += 0, 移动 j到 10 | 0 |
| 3 | 1 | 10 | 1 | 2 | 1 | 2 | 左 < 右 => res += 0, 移动i 到 2 | 0 |
| 4 | 2 | 10 | 0 | 2 | 1 | 2 | 左 < 右 => res += 1, 移动i到 3 | 1 |
| 5 | 3 | 10 | 2 | 2 | 2 | 2 | 左 >= 右 => res += 0, 移动 j到 9 | 1 |
| 6 | 3 | 9 | 2 | 1 | 2 | 2 | 左 >= 右 => res += 1, 移动 j到 8 | 2 |
| 7 | 3 | 8 | 2 | 2 | 2 | 2 | 左 >= 右 => res += 0, 移动 j到 7 | 2 |
| 8 | 3 | 7 | 2 | 3 | 2 | 3 | 左 < 右 => res += 0, 移动i到 4 | 2 |
| 9 | 4 | 7 | 1 | 3 | 2 | 3 | 左 < 右 => res += 1, 移动i 到 5 | 3 |
| 10 | 5 | 7 | 0 | 3 | 2 | 3 | 左 < 右 => res += 2, 移动 i 到 6 | 5 |
| 11 | 6 | 7 | 1 | 3 | 2 | 3 | 左 < 右 => res += 1, 移动 i 到 7 | 6 |
| 12 | 7 | 7 | 3 | 3 | 3 | 3 | 左 >= 右 => res += 0, 移动 j到 6 | 6 |