给定 n
个非负整数表示每个宽度为 1
的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
解题思路:
先取i号柱子和j号柱子(i<j)两根柱子,做以下假设:
1.i号柱子和j号柱子之间的柱子的高度均比它们两根要小。
2.i号柱子左边的柱子均比i号柱子低,或者j号柱子右边的柱子均比j号柱子高。
设min{i,j}表示的是i和j号柱子中较低的那根柱子的高度,那么任取i和j中间的某个位置k(i<k<j),则k号位置能装的最大雨水量为min{i,j}-height[k]
提示:之所以做假设2,是为了防止上图示例1中i=4,j=6的情况:
height[4]=1,heifht[6]=1,height[5]=0。满足假设1,但5号位置能装的最大水量并非1-0=0,而是2,正是因为4号柱子左边有高于它的柱子,6号柱子右边同样有高于它柱子。
为了满足以上假设,可以这样寻找i和j的位置:
设置两个指针left和right,先将left指向0,right从left+1的位置开始向右寻找,直到找到一个柱子的高度大于等于height[left](left就相当于上文中的i,right相当于j),之后便可以将left与right之间的位置尽量灌满水,然后令left=right,right=left+1,继续重复,直到right到达数组右边界。
当上述过程结束后,我们就把(height[i]<=height[j])的情况都处理好了,但忽视了(height[i]>height[j])的情况,因此可以模仿上述的过程,从数组最右端开始寻找i和j:
right指向height.length-1,left从right-1的位置开始向左寻找,直到找到一个柱子的高度大于等于height[right],之后便可以将left与right之间的位置尽量灌满水,然后令right=left,left=right-1,继续重复,直到left到达左边界。
通过从左到右,和从右到左两次遍历height数字,便可以将水灌满。
代码如下:
//从左到右装一次,再从右到左装一次
int res=0;
public int trap(int[] height) {
//从左到右
int left=0,right=1;
for (; right <height.length ; right++) {
if(height[left]>height[right]) continue;
updataLeft(height,left,right);
left=right;
}
//从右到左
right=height.length-1;
left=right-1;
for (; left>=0; left--) {
if(height[left]<height[right]) continue;
updataRight(height,left,right);
right=left;
}
return res;
}
private void updataRight(int[] height, int left, int right) {
int temp;
for (int i =right-1; i >left ; i--) {
temp=height[right]-height[i];
res+=temp;
height[i]+=temp;
}
}
private void updataLeft(int[] height, int left, int right) {
int temp;
for (int i =left+1; i <right ; i++) {
temp=height[left]-height[i];
res+=temp;
height[i]+=temp;
}
}