当青训营遇上码上掘金
「题目介绍」
攒青豆
现有 n 个宽度为 1 的柱子,给出 n 个非负整数依次表示柱子的高度,排列后如下图所示,此时均匀从上空向下撒青豆,计算按此排列的柱子能接住多少青豆。(不考虑边角堆积)
以下为上图例子的解析:
输入:height = [5,0,2,1,4,0,1,0,3]
输出:17
解析:上面是由数组 [5,0,2,1,4,0,1,0,3] 表示的柱子高度,在这种情况下,可以接 17 个单位的青豆。
「思路解析」
对于这类问题,我们不要先急着去考虑整体,而是可以从局部出发,思考一下仅仅对于某一位置来说,能够接住多少青豆?
以上述例子为例:我们将height[2]=2作为着眼点,height[2] 可以接住2个单位的青豆,因为height[2] 的高度为2,而这里最多能接住2个单位的青豆。
为什么最多能接住2个单位的青豆呢?因为,这里青豆能达到的最高高度和其左边的最高柱子、右边的最高柱子有关,我们分别称这两个柱子高度为l_max和r_max ;height[2] 最多能接住的青豆就是min(l_max,r_max)。
更进一步,对于height[i] 最多能够接住的青豆为 min( max( height[0..i] ), max( height[i..end] ) ) - height[i]
「暴力算法」
根据以上的思路,对此我们可以写一个暴力算法:
int trap(vector<int>& height)
{
int n = height.size();
int res = 0;
for (int i = 1; i < n - 1; i++) {
int l_max = 0, r_max = 0;
for (int j = i; j < n; j++)
r_max = max(r_max, height[j]);
for (int j = i; j >= 0; j--)
l_max = max(l_max, height[j]);
res += min(l_max, r_max) - height[i];
}
return res;
}
}
不难看到这个思路是很简单粗暴的,时间复杂度O(N^2),空间复杂度O(1),当然这还有可以优化的空间,我们发现计算l_max 和 r_max的方法非常麻烦。
「双指针」
因此,我们可以采用双指针的方法:
我们可以想到如果 i 处左边柱子的最大值比右边柱子的最大值要小,那么就可以不用考虑 i 处右边最大值的影响了,因为 i 处青豆量由左右柱子两侧较小值决定的。根据这个思路我们可以总结:
-
如果左侧柱子最大值<右侧柱子最大值,则青豆数由左侧最大值决定,此时可以确定左侧指针指向柱子处的青豆量,右侧指针的柱子青豆量不能确定。
-
如果左侧柱子最大值≥右侧柱子最大值,则青豆数由右侧最大值决定,此时可以确定右侧指针指向柱子处的青豆量,左侧指针的柱子青豆量不能确定。
完整代码如下: