攒青豆 | 「青训营 X 码上掘金」主题创作

87 阅读3分钟

当青训营遇上码上掘金

1.问题描述

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

image.png

以下为上图例子的解析:

输入:height = [5,0,2,1,4,0,1,0,3]  
输出:17  
解析:上面是由数组 [5,0,2,1,4,0,1,0,3] 表示的柱子高度,在这种情况下,可以接 17 个单位的青豆。

2.问题分析

拿到这个题目有些迷惑,不太确定题干意思,分析后得知

  1. 每一根柱子相当于容器的一端,必须保证两边都存在柱子才能接住青豆
  2. 不考虑边角堆积意味着把青豆看做1*1的格子即可
  3. 当青豆的高度高于两端任意一端的柱子时,就会发生溢出
  4. 求接住多少青豆,就是求攒下的青豆面积

3.解题思路一

按列来分析,以当前列h为基准,若当前列左边最高柱子为l_max,右边最高柱子为r_max,min=Min(l_max, r_max)为两边柱子高度的最小值min,则有三种情况

  1. min<h,存不住青豆
  2. min=h,存不住青豆
  3. min>h,青豆=min-h

3.1代码和实现细节

完整代码实现:code.juejin.cn/pen/7199705…

public int getDou(int[] height){
    int sum = 0, l_max, r_max, i, j, k;
    //两边的列一定不能存青豆,故而不计算
    for ( i = 1; i < height.length - 1; i ++){
        l_max = 0;
        r_max = 0;
        //左边最高
        for(j = i - 1; j >= 0; j--){
            if(height[j] > l_max)
                l_max = height[j];
        }
        //右边最高
        for(k = i + 1; k < height.length; k++){
            if(height[k] > r_max)
                r_max = height[k];
        }
        //最高的最小值
        int min = Math.min(l_max, r_max);
        //只有最小值高于当前值才会存储青豆
        if(min > height[i]){
            sum += min - height[i];
        }
    }
    return sum;
}

3.2复杂的分析

时间复杂度:O(n^2),遍历每一个列O(n),求每一个列两边的最高柱子O(n)

空间复杂度:O(1)

4.算法优化--空间换时间

根据上面的解题思路,遍历每一列都要计算一次左右的最高柱子,比较冗余,可以考虑动态规划,消耗一定的空间存储算出来的柱子最大值,即空间换时间

4.1优化思想

定义l_max (当前柱子左边的最高柱子值)和 r_max (当前柱子右边的最高柱子值)的值--计算时包括其自身。

定义两个指针left,right初始分别等于 0 和 n-1 ,left只向右边移动,right只向左边移动。

当l_h < r_h,计算left柱子所存青豆数l_max -l_h,left++,并维护l_max= Math.max(l_max, height[left]) 当l_h >= r_h,计算right柱子所存青豆数r_max -r_h,right++,并维护r_max= Math.max(r_max, height[right])

直到指针left,right相遇为止。

4.2代码和实现细节

public int getDou1(int[] height){
    int sum = 0;
    int left = 0, right = height.length-1;
    int l_max = height[0], r_max = height[height.length - 1];
    while(left < right){
        int l_h = height[left];
        int r_h = height[right];
        if(l_h < r_h){
            sum += l_max -l_h;
            left++;
            l_max = Math.max(l_max, height[left]);
        }
        else{
            sum += r_max - r_h;
            right --;
            r_max = Math.max(r_max, height[right]);
        }
    }
    return sum;
}

4.3复杂度分析

空间复杂度:O(n) 时间复杂度:O(n)

总结

该题思路与力扣上的接雨水类似,本题采用的动态规划方法用空间换时间,降低了时间复杂度,同时采用指针的思想,不使用额外数组,还是很有趣的。