当青训营遇上码上掘金-4.接青豆(接雨水)

60 阅读2分钟

当青训营遇上码上掘金

有幸加入字节的青训营,设定上如果写了相应的一些主题文章,会有额外的青豆入账。

加入了一个小队,本来以为都是大哥,进去聊了聊才发现都是本科的...而且好像只有我这个转行的是实习过的,而且参加过秋招的。

最主要的好像是还有不是很想奋进学习的...

好了,说的有点远,我这回选择的是主题4,接青豆算法的解析(实际就是接雨水...接豆子这意思还挺合适)

  • 主题 4:攒青豆

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

上面是题意,我就不去分析题目了,直接给出几个解法,本人的主要学习语言是Java,所以就给出Java的算法了。

1.双指针解法

public static int trap(int[] height) {
    //按照每一列来算
    int sum=0;
    for(int i=1;i<height.length-1;i++){
        //左侧最大值
        int left=-1;
        //右侧最大值
        int right=1;
        int now=height[i];
        for(int j=0;j<i;j++){
            left=Math.max(left,height[j]);
        }
        for(int j=i+1;j<height.length;j++){
            right=Math.max(right,height[j]);
        }
        //当前列存的值
        int h = Math.min(left, right) - height[i];
        if(h>0){
            sum+=h;
        }
    }
    return sum;
}

思路是对每个空位(柱子)进行遍历,分别来判断左边的最大值和右边的最大值,那么当前能存的值就是左右最大值的最小值减去当前柱子的高度,当把所有的柱子(不包括第一个和最后一个)都遍历一遍,最终能获得的所有豆子(雨水)就是答案。

2.动态规划

public static int trap1(int[] height){
    //动态规划
    //dp[][]
    //dp[i][j]  i 表示位置 j-0代表左侧的最大值 1代表右侧的最大值
    int[][] dp=new int[height.length][2];
    //赋值初始值
    //i==0
    dp[0][0]=0;
    dp[0][1]=-1;
    for(int i=1;i<height.length;i++){
        dp[0][1]=Math.max(height[i],dp[0][1]);
    }
    //i==height.lenght
    dp[height.length-1][0]=-1;
    dp[height.length-1][1]=0;
    for(int i=height.length-1;i>=0;i--){
        dp[height.length-1][0]=Math.max(height[i],dp[height.length-1][0]);
    }
    //递推公式
    //左边最大值的递推公式
    for(int i=1;i<height.length;i++){
        dp[i][0]=Math.max(dp[i-1][0],height[i-1]);
    }
    //右边最大值的递推公式
    for(int i=height.length-2;i>=0;i--){
        dp[i][1]=Math.max(dp[i+1][1],height[i+1]);
    }
    //sum
    int sum=0;
    for(int i=1;i<height.length-1;i++){
        int count = Math.min(dp[i][0], dp[i][1]) - height[i];
        if(count>0){
            sum+=count;
        }
    }
    return sum;
}

核心就是动态数组如何进行设计,我没有进行简化,这题目是可以简化为一维滚动数组的。

dp[i][j] i当前所在位置,也就是柱子

j[2]为两个空位的数组

j[0]为左侧的最大值 j[1]为右侧的最大值

接下来就是递推

ok,到此结束!