42. 接雨水
- 1)题目
- 2)思路
- 3)代码
- 4)结果
1)题目
给定 n
个非负整数表示每个宽度为 1
的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
示例 1:
输入:height = [0,1,0,2,1,0,1,3,2,1,2,1]
输出:6
解释:上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。
示例 2:
输入:height = [4,2,0,3,2,5]
输出:9
提示:
- n == height.length
- 1 <= n <= 2 * 104
- 0 <= height[i] <= 105
2)思路
初始指针:左指针放第一个位置,右指针放左指针后面一个位置
当左指针大于右指针时,右指针往后移,直到移动到右指针大于左指针为止;
当左指针小于右指针时,计算该区间内的水滴数,之后左指针移动到右指针的位置,右指针移动到左指针之后。
由此左低右高的柱子区间水滴数统计完成
0 1 2 3 4 5 6 7 8 9 10 11
height = [0, 1, 0, 2, 1, 0, 1 ,3, 2, 1, 2, 1]
l0 r1 l<r l=r r=r+1
l1 r2 l>r r++
l1 r3 l<r (r-l-1)*l1-(r2) l=r r=r+1
l3 r4 r5 r6 l>r r++ ...
l3 r7 l<r (r-l-1)*l3-(r4+r5+r6) l=r r=r+1
l7 r8 r9 r10 r11 l>r r++ ... r=len 结束
(r-l-1)*l1-(r2) = (3-1-1)*1-0 = 1
(r-l-1)*l3-(r4+r5+r6) = (7-3-1)*2-(1+0+1) = 4
反过来统计左高右低的柱子区间水滴数,注意l<=r
height = [0, 1, 0, 2, 1, 0, 1 ,3, 2, 1, 2, 1]
l10 r11 l>r r=l l=r-1
l9 r10 l<r l--
l8 r10 l=r l--
l7 r10 l>r (r-l-1)*r10-(r9+r8) r=l l=r-1
l0 l1 l2 l3 l4 l5 l6 r7 l<r l-- ... l=0 结束
(r-l-1)*r10-(r9+r8) = (10-7)*2-(1+2) = 1
水滴数 = 1+4+1 =6
3)代码
public static int trap(int[] height) {
if (height.length < 3) return 0;
int left = 0, right = 1;
int num = 0;
while (left < height.length - 1 && right < height.length) {
if (height[left] > height[right]) {
right++;
} else {
num += getNum(height, left, right, num);
left = right;
right = left + 1;
}
}
left = height.length-2;
right = height.length-1;
while (right > 0 && left >= 0) {
if (height[left] <= height[right]) {
left--;
} else {
num += getNum(height, left, right, num);
right = left;
left = right - 1;
}
}
return num;
}
/**
* 获取两个柱子之间接水的数量
* @return (r-l-1)*min(l,r)-中间值之和
*/
private static int getNum(int[] height, int left, int right, int num) {
int middleLen = right - left - 1;
int middleValueNum = 0;
for (int i = 1; i <= middleLen; i++) {
middleValueNum += height[left + i];
}
int min = height[left] < height[right] ? height[left] : height[right];
return middleLen * min - middleValueNum;
}