接雨水
给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。 感谢 Marcos 贡献此图。
示例:
输入: [0,1,0,2,1,0,1,3,2,1,2,1]
输出: 6
分析
观察上面的图,可以发现积水坐标 i 都满足条件
- height[i-1] > height[i]
- height[i] < height[i+1]
进一步考虑,如何处理去掉上面的两个条件之一,只需考虑单一条件。
假定已知最高点的坐标maxIndex及其高度maxValue,考虑从左逐渐靠近最高点,此时只需考虑上面的条件1,因为无论如何何时,始终有:
- height[maxIndex] >= height[i]
即,始终右侧足够高。
所以从两端向中间靠拢时,只需找局部最高curRoot即可,局部最高与其余高度值的差值和即为积水和。
对于给定例子: [0,1,0,2,1,0,1,3,2,1,2,1]:
- 最高值为3,其坐标为7
- 以0~6高度值为例,局部最高分别为:[0,1,1,2,2,2,2] (从左向右)
- curRoot - height[i] 分别为 [0,0,1,0,1,2,1]
- 于是左部分积水和为 1+1+2+1 = 5,结合图验证此结果正确。
右部分省略。
于是,只需三次遍历即可
- 遍历整个数组,找最高点
- 从左向右遍历至最高点坐标,求积水
- 从右向左遍历至最高点坐标,求积水
时间复杂度为O(n),以及常数空间复杂度。
代码
class Solution {
public int trap(int[] height) {
if(height.length<=2){
return 0;
}
int maxIndex=0, maxValue = 0;
int res = 0;
for(int i=0;i<height.length;i++){
if(height[i]>=maxValue){
maxValue = height[i];
maxIndex = i;
}
}
int curRoot = height[0];
for(int i =0;i<maxIndex;i++){
if(curRoot<height[i]){
curRoot = height[i];
}else{
res += curRoot-height[i];
}
}
curRoot = height[height.length-1];
for(int i=height.length-1;i>maxIndex;i--){
if(curRoot<height[i]){
curRoot = height[i];
}else{
res += curRoot-height[i];
}
}
return res;
}
}