当青训营遇上码上掘金
有幸加入字节的青训营,设定上如果写了相应的一些主题文章,会有额外的青豆入账。
加入了一个小队,本来以为都是大哥,进去聊了聊才发现都是本科的...而且好像只有我这个转行的是实习过的,而且参加过秋招的。
最主要的好像是还有不是很想奋进学习的...
好了,说的有点远,我这回选择的是主题4,接青豆算法的解析(实际就是接雨水...接豆子这意思还挺合适)
-
主题 4:攒青豆
现有 n 个宽度为 1 的柱子,给出 n 个非负整数依次表示柱子的高度,排列后如下图所示,此时均匀从上空向下撒青豆,计算按此排列的柱子能接住多少青豆。(不考虑边角堆积)
以下为上图例子的解析:
输入: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,到此结束!