当青训营遇上码上掘金之攒青豆

63 阅读3分钟

当青训营遇上码上掘金之攒青豆

当青训营遇上码上掘金

题目分析

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

要想知道能接住多少豆子,我们可以按照列的形式先进行计算。即我们计算每一列可以接多少豆子,之后再将它们相加即可。

怎么判断每一列是否可以接豆子呢?从图中可以看出:当前列能不能接豆子,取决于左右两边是否都有比当前列高的柱子,如果有的话,那么就一定可以接到豆子。那如何计算可以接多少呢?这就取决于左右最高柱子的短板了,计算公式为:(左边最高柱子高度,右边最高柱子高度)取更小的那一个,减去当前柱子的高度即可。

有了思路,我们下面直接上代码

public static int saveBeans1(int[] height){
    int len=height.length;
    int[] left=new int[len];
    int[] right=new int[len];
    //计算left数组
    for(int i=1;i<len;i++){
        left[i]=Math.max(left[i-1],height[i-1]);
    }
    //计算right数组
    for(int i=len-2;i>=0;i--){
        right[i]=Math.max(right[i+1],height[i+1]);
    }
    int ans=0;
    for(int i=1;i<len-1;i++){
        int minHeight=Math.min(left[i],right[i]);
        if(minHeight>height[i]) ans+=minHeight-height[i];
    }
    return ans;
}

left[i]和right[i]分别代表下标i之前最高柱子的高度和下标i之后最高柱子的高度。minHeight就代表我们之前提到的(左边最高柱子高度,右边最高柱子高度)中的短板。

由于我们使用了left数组和right数组,所以空间复杂度为O(N)

空间优化

回看这道题目,仔细思考一下:我们真的需要left数组和right数组吗?

在计算每一列的时候,我们只需要它左边柱子的最大高度和右边柱子的最大高度,这里我们先用leftMax和rightMax指代。如果我们还是依照从左往右的遍历顺序进行遍历,那么leftMax可以正常计算,而rightMax还是需要嵌套一层循环进行计算,这样时间复杂度就上升到平方级别了。因此我们不再使用一个指针,而是借助双指针,从两边向中间收缩,那么leftMax和rightMax都可以正常计算。

那么这里计算的leftMax和rightMax具体的意义是什么呢?我们先用left和right表示两个指针,leftMax即left下标左边(包括当前位置)的柱子最大高度,rightMax即right下标右边(包括当前位置)的柱子最大高度。

这里有几个隐含条件:1.左指针的leftMax<=右指针的leftMax 2.左指针的rightMax>=右指针的rightMax

因此,当左指针的leftMax<右指针的rightMax时,由条件2即可推出左指针的左指针的leftMax<左指针的rightMax,这样就可以计算左指针位置的可接青豆数;同理,当条件相反时,可计算右指针位置的可接青豆数。

思路有了,代码如下:

public static int saveBeans2(int[] height){
    int len=height.length;
    int left,right;
    left=0;
    right=len-1;
    int leftMax=0,rightMax=0;
    int ans=0;
    while(left<right){
        leftMax=Math.max(height[left],leftMax); //左指针的leftMax
        rightMax=Math.max(height[right],rightMax); //右指针的rightMax
        if(leftMax<rightMax){
            //左指针的leftMax<右指针的rightMax -> 左指针的leftMax<左指针的rightMax
            //左指针所属位置青豆计算
            ans+=leftMax-height[left];
            left++;
        } else{
            ans+=rightMax-height[right];
            right--;
        }
    }
    return ans;
}

总结

其实这道题目就是力扣上的接雨水,只是换了个说法而已。对于大多数读者,估计第一想法就是解法一(数组);空间优化这种解法确实不好理解,如果还是不懂,可以自己在草稿纸上把几个规则列出来,配合着画图,还是能够啃下来的。