【青训营X码上掘金】主题创作活动--主题四:攒青豆

73 阅读3分钟

【青训营X码上掘金】主题创作活动--主题四:攒青豆

当青训营遇上码上掘金

1、主题介绍

现有 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 个单位的青豆。

2、创作思路

  • 思路一:暴力遍历法

    思路一较为简单。我们可以依次遍历表示柱子高度的数组,每当我们读到一个高度时,找到该柱子左右两边的最高值,然后取左右两边最高值间更小的那个,再减去当前柱子高度,就是该空间可以攒下的青豆。

    注意:我们只需要从第二个元素开始遍历,因为第一个柱子不可能攒下青豆,最后一个柱子同理。

    样例分析:

    • 第一个读到的柱子高度为0,此时它左边柱子最高值为5,右边柱子最高值为4,取两者更小值4,当前空间攒下的青豆为4-0=4青豆。
    • 第二个读到的柱子高度为2,此时它左边柱子最高值为5,右边柱子最高值为4,取两者更小值4,当前空间攒下的青豆为4-2=2青豆。
    • ......
    • 最后将青豆累加,得到最终结果17。
  • 思路二:双指针遍历法

    双指针即从数组左右两边向中间遍历,可以降低时间复杂度。

    双指针主要思路如下:

    • 第一步:记录左右两边柱子的最大高度。
    • 第二步:定义双指针由数组两边向中间遍历。
    • 第三步:如果双指针对应的数组的值大于最大高度,替换最大高度;然后计算当前空间青豆数。
    • 第四步:当双指针指向同一位置时,得到最终结果。

3、代码实现

  • 思路一

    //暴力遍历法
    public static int getGreenBean1(int[] nums){
    ​
        int tmp = 0;
        int res = 0;
    ​
        for (int i = 1;i < nums.length;i++){
            tmp = getLeftMax(i,nums) < getRightMax(i,nums) ? getLeftMax(i,nums) : getRightMax(i,nums);
            res += tmp - nums[i];
        }
    ​
        return res;
    ​
    }
    ​
    public static int getLeftMax(int index,int[] nums){
    ​
        int res = 0;
    ​
        for (int i = index;i >= 0;i--){
            if (nums[i] > res)
                res = nums[i];
        }
    ​
        return res;
    ​
    }
    ​
    public static int getRightMax(int index,int[] nums){
    ​
        int res = 0;
    ​
        for (int i = index;i < nums.length;i++){
            if (nums[i] > res)
                res = nums[i];
        }
    ​
        return res;
    ​
    }
    
  • 思路二

    //双指针遍历法
    public static int getGreenBean2(int[] nums){
    ​
        int res = 0;
        int left = 0;
        int right = nums.length - 1;
        int leftMax = 0;
        int rightMax = 0;
    ​
        while (left < right){
            if (nums[left] < nums[right]){
                if (nums[left] > leftMax)
                    leftMax = nums[left];
                res +=  leftMax - nums[left++];
            }else {
                if (nums[right] > rightMax)
                    rightMax = nums[right];
                res += rightMax - nums[right--];
            }
        }
    ​
        return res;
    ​
    }
    

完整代码见码上掘金:

完整代码

4、对比分析

  • 时间复杂度

    暴力遍历法每读到一个高度,都要遍历数组找到左右两边的最大高度;而双指针遍历则不用,只需要遍历一遍整个数组即可得出结果。双指针遍历法的时间复杂度是远远小于暴力遍历法的。

  • 空间复杂度

    双指针需要创建的临时变量比较多,而暴力遍历法的临时变量只有一个。所以暴力遍历法的空间复杂度优于双指针遍历法。

  • 总结

    本次主题创作主要是涉及数组的相关知识,以上解法都是比较浅显的解法。如果大家有更好的解法,我也会认真学习。同时,也欢迎大家阅读,以及提出宝贵意见。