问题描述
给定 n 个非负整数(int[] height)表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
数据范围:
n == height.length
1 <= n <= 2 * 104
0 <= height[i] <= 105
思路与方法
观察可知要想收集雨水必须在呈现山谷的的区域,也就是两个山峰之间,并且两山峰之间有两种情况,一是左峰顶低于或者等于于右峰顶,此时可以直接计算这两个峰顶之间谷底的雨水量,以左峰顶的高度减去两峰之间每列(需要小于左峰顶)的高度即可;另一种情况是左峰顶高于右峰顶,此时从右峰顶往左计算雨水量,即用右峰顶的高度减去两峰顶之间每列(需要小于右峰顶)的高度。另外可以在到达与小峰顶值相等或者大于小峰顶值时停下,而不必遍历到大峰顶。
Code
public int trap(int[] height) {
int left=0;int len=height.length;int rain=0;
for(left=0;left<height.length-1;left++){
if(height[left]>height[left+1]){//找到左峰顶
int right=left+1;
while(right<len&&height[right]<height[left]){//若当前高度大于等于左峰顶
//则已经可以直接计算当前高度到左峰顶的雨水量
int before=height[right-1];
int after=right<len-1?height[right+1]:0;
if(height[right]>before&&height[right]>=after){//右边峰顶(小于左峰顶)
int i=right-1;
while(height[i]<height[right]){//右峰顶到左边齐平位置的雨水量
rain+=(height[right]-height[i]);
height[i]=height[right];i--;
}
}
right++;
}
if(right==len){//表示走到了末位退出而不是找到了大于等于左峰顶的位置,直接返回结果
return rain;
}
//找到了大于等于左峰顶的位置,从左峰顶到右边(不一定是峰顶但是大于左峰顶)计算雨水量
int i=left+1;
while(height[i]<height[left]){
rain+=(height[left]-height[i]);
height[i]=height[left];i++;
}
left=right-1;//移动指针到该位置
}
}
return rain;
}
复杂度分析
寻找峰顶一趟遍历,收集雨水一趟遍历,总共两趟遍历(实际上不到),所以时间复杂度为O(n),内存上仅用了常数量级的变量,所以空间复杂度为O(1)。