Leetcode题解(3):接雨水
1. 动态规划
对于任意一个条形块 height[i] ,能蓄水的必要条件是存在 i>l>0 ,i<r<n 使得 height[l]>height[i] 并且 height[r]>height[i] 。当上述条件成立时,这个条形块的蓄水量就等于 min(height[l], height[r])−height[i] 。所以我们可以记录每个条形块对应的左右两边最高的条形块,然后每次遍历时比较左右两边的最高条形块,并取其中较小者。
class Solution {
public int trap(int[] height) {
int n = height.length;
if (n == 0) return 0;
int[] left = new int[n];
int[] right = new int[n];
left[0] = height[0];
right[n - 1] = height[n - 1];
for (int i = 1; i < n; i++)
left[i] = Integer.max(height[i], left[i - 1]);
for (int i = n - 2; i >= 0; i--)
right[i] = Integer.max(height[i], right[i + 1]);
int res = 0;
for (int i = 1; i < n - 1; i++)
res += (Integer.min(left[i], right[i])) - height[i];
return res;
}
}
2. 双指针
观察上面的 left[ ] 和 right[ ] ,可以发现它们元素的状态仅依赖于其左边或者右边元素的状态以及 height[ ] ,我们可以利用这个特性,将数组优化掉。
对于每个 i ,只要 left[i]>height[i] ,right[i]>height[i] ,并且 left[i]<right[i] ,那么就由左边条形柱决定蓄水量,否则就由右边决定。也就是说,对于每个 height[i] ,只要其左右两边存在 left−max>height[i] 并且 right−max>height[i] 即可蓄水。所以我们可以记录一个 left−max 和一个 right−max ,然后利用双指针遍历更新。指针移动规则是:
- 如果 height[l]<height[r] ,则移动左指针;
- 否则,移动右指针。
采用上述的更新规则后,左右指针中的一个会最终指向最高的条形柱并固定不动。代码实现如下:
class Solution {
public int trap(int[] height) {
int l = 0, r = height.length - 1, res = 0, left_max = 0, right_max = 0;
while (l <= r) {
if (height[l] < height[r]) {
if (height[l] > left_max) left_max = height[l];
else res += left_max - height[l];
l++;
} else {
if (height[r] > right_max) right_max = height[r];
else res += right_max - height[r];
r--;
}
}
return res;
}
}