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

94 阅读2分钟

当青训营遇上码上掘金

主题4-攒青豆

题目描述

现有 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个柱子组成分别为柱子n以及柱子n向左或向右找到第一个的比柱子n高的柱子,如果没有就是仅比柱子n低的柱子。然后用长*宽-两个支柱之间的所有柱子高度,便可算出该区间可以接收的青豆数,总青豆数=所有区间可以接收的青豆数之和

题目描述中支柱从左到右分析分别为第1个黑柱子(5),第4个黑柱子(4),第6个黑柱子(3),总计3个支柱组成的2个区间

所以总青豆数=3421+3313*4-2-1+3*3-1=17

暴力法

最简单的暴力法,思想为柱子1开始从左往右,找第一个比该柱子大的柱子n,如果没有就找仅比该柱子小的柱子n,以这两个柱子之间为区间计算青豆数,之后以柱子n为起点继续往右寻找,循环往复直到所有区间找完,此方法当遇到柱子大小为(n,n-1,n-2,n-3.....1)这种逐级递减时,时间复杂度最大为O(n^2)

优化方案

如果每个柱子都知道其左右两边的最大高度,那么计算时就可以降低时间复杂度,因此我们尝试先遍历数组计算出每个柱子左右的最大高度,然后利用生成的两个数组,来计算出每个位置能存多少青豆。此时时间复杂度为O(n)

class Main {
    public static int compute(int[] heights) {
        int n = heights.length;
        if (n == 0) {
            return 0;
        }

        int[] leftMax = new int[n];//记录每个柱子左边的最大高度
        leftMax[0] = heights[0];
        for (int i = 1; i < n; ++i) {
            leftMax[i] = Math.max(leftMax[i - 1], heights[i]);
        }

        int[] rightMax = new int[n];//记录每个柱子右边的最大高度
        rightMax[n - 1] = heights[n - 1];
        for (int i = n - 2; i >= 0; --i) {
            rightMax[i] = Math.max(rightMax[i + 1], heights[i]);
        }

        int nums = 0;//青豆数
        for (int i = 0; i < n; ++i) {
            nums+= Math.min(leftMax[i], rightMax[i]) - heights[i];
        }
        return nums;
    }
    public static void main(String[] args) {
        int[] heights=new int[]{5,0,2,1,4,0,1,0,3};
        System.out.println(compute(heights));
    }
}