当青训营遇上码上掘金
主题 4:攒青豆:
题目描述:
现有 n 个宽度为 1 的柱子,给出 n 个非负整数依次表示柱子的高度,排列后如下图所示,此时均匀从上空向下撒青豆,计算按此排列的柱子能接住多少青豆。(不考虑边角堆积)
以下为上图例子的解析:
输入:height = [5,0,2,1,4,0,1,0,3]
输出:17
解析:上面是由数组 [5,0,2,1,4,0,1,0,3] 表示的柱子高度,在这种情况下,可以接 17 个单位的青豆。
这道题是一道很经典的接雨水问题改编的,我们先来看看单独一个柱子它能接到多少青豆。首先最左边和最右边的柱子有一侧是没有柱子的,肯定是接不到青豆的。我们现在假设有三根柱子,求最中间那根柱子能接到多少青豆:
很显然,如果最中间的柱子比左右两个柱子的其中一个都要高,肯定会从比他短的柱子那漏光,能接0个青豆。中间柱子比两边的矮,那么一直往上堆最终会从左右两边最短的那个柱子漏掉,能接min(左边柱子高度,右边柱子高度)-当前柱子高度 个青豆。那么就可以从这三根柱子的状态推广到N个柱子的情况,第X根柱子能接的豆为min(左边柱子最高的高度度,右边柱子最高的高度)-当前柱子高度,如果结果小于0,就取0;
但是如果按顺序遍历,每次都要求左边柱子最高的高度度和右边柱子最高的高度,这样时间复杂度很高,这里可以利用双指针的方法。分别从柱子数组的左右两端开始找,不断记录左右两端的最大值就行了。通过指针我们可以知道在该指针左边柱子最大值和右边一部分最大值和另一端右边最大值和左边部分最大值,通过前面的推论可知我们需要找最小值,当我们明确的一边的最大值要小于另一边一部分的最大值时那么我没就得到了我们想要的最小值,就都那边的指针。当两个指针相遇时,即可得到能接的雨水总量。
C语言的代码:
int trap(int* height, int heightSize){
int l=height[0],r=height[heightSize-1],ans=0,ln=1,rn=heightSize-2;
while(ln<=rn){
if(l<r){
if(height[ln]<=l) ans+=l-height[ln];
else l=height[ln];
++ln;
}else{
if(height[rn]<r) ans+=r-height[rn];
else r=height[rn];
--rn;
}
}
return ans;
}