42. 接雨水
题目链接
暴力枚举
- 枚举每个点,分别找左边和右边的最大值,二者中较小的就是这个点的深度
- 每个点深度之和就是结果
class Solution {
public int trap(int[] height) {
int len = height.length;
int ans = 0;
for(int i = 1; i < len - 1; i++){
int maxLeft = 0, maxRight = 0;
for(int j = i; j >= 0; j--)
maxLeft = Math.max(maxLeft, height[j]);
for(int j = i; j < len; j++)
maxRight = Math.max(maxRight, height[j]);
ans += Math.min(maxLeft, maxRight) - height[i];
}
return ans;
}
}
预处理
- 显然上面的方法有优化空间,因为我们没有必要每个点都重新计算左右最大值
- 我们可以通过预处理的方式,分别用一次循环算出左右最大值
class Solution {
public int trap(int[] height) {
int len = height.length;
if(len == 0) return 0;
int[] maxLeft = new int[len];
int[] maxRight = new int[len];
maxLeft[0] = height[0];
for(int i = 1; i < len; i++)
maxLeft[i] = Math.max(maxLeft[i-1], height[i]);
maxRight[len-1] = height[len-1];
for(int i = len - 2; i >= 0; i--)
maxRight[i] = Math.max(maxRight[i+1], height[i]);
int ans = 0;
for(int i = 1; i < len - 1; i++)
ans += Math.min(maxLeft[i], maxRight[i]) - height[i];
return ans;
}
}
双指针
- 能不能用一次循环,就求出结果?
- 理论上是可行的,因为其实我们可以在一次循环中求出路过的最大值,同时求出当前点的深度
- 但是,如果只是单向循环,我们没有办法同时找出两边的最大值
- 因此,我们使用双指针,分别从两边往中间枚举
- 对于左指针来说,左边的leftMax是可信的,而右边的rightMax是不可信的,因为没有枚举所有的右边高度
- 但是,当左指针的高度小于右指针的高度时,右边就不可能出现比左边小的rightMax了,即当前点的深度只取决于leftMax,直接计算结果即可
- 右指针同理,当右指针高度小于左指针时,深度取决于rightMax
class Solution {
public int trap(int[] height) {
int len = height.length;
if(len == 0) return 0;
int ans = 0;
int left = 0, right = len - 1;
int leftMax = 0, rightMax = 0;
while(left < right){
if(height[left] < height[right]){
if(height[left] > leftMax)
leftMax = height[left];
else ans += leftMax - height[left];
left++;
} else {
if(height[right] > rightMax)
rightMax = height[right];
else ans += rightMax - height[right];
right--;
}
}
return ans;
}
}