42. 接雨水
给定 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.length
1 <= n <= 2 * 10^4
0 <= height[i] <= 10^5
解题
方法一:动态规划
对于下标 i,下雨后水能到达的最大高度等于下标 i 两边的最大高度的最小值,下标 i 处能接的雨水量等于下标 i 处的水能到达的最大高度减去 height[i]。
动态规划是用两个数组记录当前位置的左边遇到的最高高度,右边遇到的最高高度, 然后取left right 两者中较小的 减去height[i] 就是当前位置水的高度。
时间n,空间n
class Solution {
public int trap(int[] height) {
int length = height.length;
int[] leftHeight = new int[length];
int[] rightHeight = new int[length];
// 获取当前位置左边的最大高度
leftHeight[0] = height[0];
for (int i = 1; i < length; i++) {
leftHeight[i] = Math.max(leftHeight[i-1], height[i]);
}
// 获取当前位置右边的最大高度
rightHeight[length-1] = height[length-1];
for (int i = length-2; i >= 0; i--) {
rightHeight[i] = Math.max(rightHeight[i+1], height[i]);
}
// 计算答案
int ans = 0;
for (int i = 0; i < length; i++) {
ans += Math.min(leftHeight[i], rightHeight[i]) - height[i];
}
return ans;
}
}
方法二:双指针
动态规划是用两个数组记录当前位置的左边遇到的最高高度,右边遇到的最高高度,然后取left right 两者中较小的 减去height[i] 就是当前位置水的高度。 在此基础上,省去两个数组,改为两个指针,一个在最左,一个在最右。 相向而动,同时记录遇到的最大值,比较谁小,谁移动,同时计算这一块的雨水。
时间n,空间1
class Solution {
public int trap(int[] height) {
int length = height.length;
int left = 0;
int right = length-1;
int leftMax = 0;
int rightMax = 0;
int ans = 0;
// 相向而动
while (left < right) {
leftMax = Math.max(leftMax, height[left]);
rightMax = Math.max(rightMax, height[right]);
if (leftMax < rightMax) {
ans += leftMax - height[left];
left++;
} else {
ans += rightMax - height[right];
right--;
}
}
return ans;
}
}