小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
原题:42. 接雨水
给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。
如上图中的示例,最终的结果为 6。
解题思路:
我们可以把所有能积攒的雨水,按照柱子的下标进行逐个计算,每个下标处可以积攒的雨水的高度,等于左右两边最高的柱子中更低的一个柱子的高度,再减去当前柱子的高度。我们可以遍历每一个柱子,再找出当前柱子左右两边最高的柱子,最终求出每个下标可以积攒的雨水,最终把所有计算结果加起来即可。
但是这种暴力算法肯定不是我们要的答案。
我们可以从左向右遍历,便利的过程中,记录柱子高度的最大值,这样,每当我们便利到一个柱子的时候,都知道它左边最高的柱子是多高。然后,在右边进行同样的操作。
我们采用双指针的方法,通过 l 和 r 指针,分别从最左边和最右边向中间开始移动,左指针 l 指向的元素,它的左侧最大值是确认的,右侧亦然。
因此,在任意位置,如果左侧最大值小于右侧最大值,那么我们可以计算所指针的当前位置可以积攒的水量。因为左侧最大值是确认的,右侧最大值虽然不缺人,但是一定比左侧最大值打,因此,此时坐指针指向的元素的积水量就是左侧最大值减去当前位置柱子的高度,(由于可能出现当前位置柱子比最大值高,计算结果与 0 取较大值)计算完并累加结果之后,更新左侧最大值并向右移动左侧指针。
右边进行相同的操作,当左右指针相遇时,就得到了总积水量
最终代码:
class Solution {
public int trap(int[] height) {
int n = height.length;
if (n < 3) {
return 0;
}
int l = 0, r = n - 1, lmax = 0, rmax = 0;
int area = 0;
while (l <= r) {
if (lmax < rmax) {
area += Math.max(0, lmax - height[l]);
lmax = Math.max(lmax, height[l]);
l++;
} else {
area += Math.max(0, rmax - height[r]);
rmax = Math.max(rmax, height[r]);
r--;
}
}
return area;
}
}