当青训营遇上码上掘金, 青训营主题四-攒青豆
现有 n 个宽度为 1 的柱子,给出 n 个非负整数依次表示柱子的高度,排列后如下图所示,此时均匀从上空向下撒青豆,计算按此排列的柱子能接住多少青豆。(不考虑边角堆积)
以下为上图例子的解析:
输入:height = [5,0,2,1,4,0,1,0,3]
输出:17
解析:上面是由数组 [5,0,2,1,4,0,1,0,3] 表示的柱子高度,在这种情况下,可以接 17 个单位的青豆。
分析
根据题目可以得出,一个柱子上方是否可以接豆子,取决于该柱子两边的柱子是否可以在该柱子处形成凹槽,而决定能否形成凹槽的因素是两边存在比该柱子更高的柱子,而具体能存放多少豆子,则取决于两边最高的柱子中较低的那个柱子,与该柱子高度的差值。
因此,可通过遍历数组记录每个柱子左边和右边最高的柱子的高度,若左右两边最高的柱子中较小的那个的高度比该柱子高度大,则可以存放豆子,存放豆子数为左右两边最高的柱子中较小的那个的高度与该柱子高度的差值,反之则不可以存放豆子。
某个柱子左边的柱子的最大高度从所有柱子的最左边的柱子开始遍历记录,右边的柱子的最大高度从所有柱子的最右边的柱子开始遍历并记录,由于不考虑边角堆积,故可将最左边柱子的左边柱子的最大高度记为0,最右边柱子同理,即可达到边角不堆积的效果
使用两个一维数组,leftMaxHeight,rightMaxHeight来记录某个位置左边和右边高度最高的柱子的高度,两轮遍历记录最大高度,第三轮遍历记录每个柱子上方可接的青豆数量并累加。
代码实现
public class Main {
public static void main(String[] args) {
int[] height = { 5, 0, 2, 1, 4, 0, 1, 0, 3};
int sum = calculate(height);
System.out.println(sum);
}
public static int calculate(int[] height) {
if (height == null || height.length <= 2) return 0;//柱子数小于3无法接豆子
int sum = 0;//记录青豆的数量
int len = height.length;
int[] leftMaxHeight = new int[len];//记录某个柱子左边的柱子的最大高度
int[] rightMaxHeight = new int[len];//记录某个柱子右边的柱子的最大高度
leftMaxHeight[0] = 0;//第一个柱子左边的柱子的最大高度为0,故遍历从i=1开始
for (int i = 1; i < len; i++) {
leftMaxHeight[i] = Math.max(leftMaxHeight[i - 1], height[i - 1]);//i左边柱子的最大高度由i-1左边柱子的最大高度和i-1本身的高度的最大值决定
}
rightMaxHeight[len-1] = 0;//最后一个柱子右边的柱子的最大高度为0,故遍历从i=len-2开始
for (int i = len - 2; i >= 0; i--) {
rightMaxHeight[i] = Math.max(rightMaxHeight[i + 1], height[i + 1]);//i右边柱子的最大高度由i+1右边柱子的最大高度和i+1本身的高度的最大值决定
}
for (int i = 1; i < len - 1; i++) {
int minHeight = Math.min(leftMaxHeight[i], rightMaxHeight[i]);
//如果i柱子上方能够攒青豆,那么攒数量由两边的最高柱子中较小的柱子的高度决定
if (minHeight > height[i]) {
//攒的数量为i两边的最高柱子中较小的柱子的高度与i的高度差
sum += minHeight - height[i];
}
}
return sum;
}
}
代码链接码上掘金