「青训营 X 码上掘金」主题 4:攒青豆

68 阅读2分钟

当青训营遇上码上掘金

「题目介绍」

攒青豆

现有 n 个宽度为 1 的柱子,给出 n 个非负整数依次表示柱子的高度,排列后如下图所示,此时均匀从上空向下撒青豆,计算按此排列的柱子能接住多少青豆。(不考虑边角堆积)攒青豆.png

以下为上图例子的解析:
输入: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_maxr_maxheight[2] 最多能接住的青豆就是min(l_max,r_max)2.jpg 更进一步,对于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_maxr_max的方法非常麻烦。

「双指针」

因此,我们可以采用双指针的方法:

我们可以想到如果 i 处左边柱子的最大值比右边柱子的最大值要小,那么就可以不用考虑 i 处右边最大值的影响了,因为 i 处青豆量由左右柱子两侧较小值决定的。根据这个思路我们可以总结:

  1. 如果左侧柱子最大值<右侧柱子最大值,则青豆数由左侧最大值决定,此时可以确定左侧指针指向柱子处的青豆量,右侧指针的柱子青豆量不能确定。

  2. 如果左侧柱子最大值≥右侧柱子最大值,则青豆数由右侧最大值决定,此时可以确定右侧指针指向柱子处的青豆量,左侧指针的柱子青豆量不能确定。

完整代码如下: