当青训营遇上码上掘金,会碰撞出怎样的火花?
一、题目引入
主题 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 个单位的青豆。
二、方法引入
双指针算法
严格来说,双指针法应该被理解为思想而不是算法。所谓双指针,指的是在遍历对象的过程中,不是普通的使用单个指针进行遍历访问,而是使用两个相同方向(快慢指针)或者相反方向(对撞指针)的指针进行扫描,从而达到相应的目的。
最常见的双指针算法有两种:一种是在一个序列里边,用两个指针维护一段区间;另一种是,在两个序列里边,一个指针指向其中一个序列,另外一个指针指向另外一个序列,来维护某种次序。
三、问题分析
首先,对于某一个位置p, 我们能否接这个位置上的青豆, 取决于p左右两侧的最大值leftMax和rightMax是否比height[p]大. 当且仅当i左右两侧的最大值都比height[p]大时, 才能接住i的青豆, 数量为min(leftMax,rightMax)-height[p];
其次,设置两个指针p和q, p从左向右移动, q从右向左移动, 对于指针q, leftMax是可靠的, 因为leftMax的值是p遍历的,而rightMax是不可靠的, 因为p无法确定从height[p]到height[q]之间是否存在其他位置的值大于rightMax; 同理,对于q来说rightMax是可靠的, leftMax的值是不可靠的.
综合以上分析,对于一个位置p而言影响其接青豆的变量是是左右两侧最大值中的较小值, 即min(leftMax, rightMax), 又因为左指针右侧的可靠最大值大于等于rightMax, 故当出现leftMax < rightMax的情况时, 左指针的位置是否能接青豆是确定的;同理, 当出现leftMax >= rightMax的情况时, 右指针的位置是否能接青豆也是确定的。由以上分析,不难写出相应的代码。
四、代码参考
public class Gathergreenbeans {
public static void main(String[] args) {
int[] height = {5, 0, 2, 1, 4, 0, 1, 0, 3};
System.out.printf("上面是由数组[5,0,2,1,4,0,1,0,3]表示的柱子高度," +
"在这种情况下,可以接%d个单位的青豆", gathergreenbeans(height));
}
public static int gathergreenbeans(int[] height) {
//初始化
int sum = 0, n = height.length, leftMax = height[0], rightMax = height[n - 1];
int p = 0, q = n - 1;
//寻找前缀最大值和后缀最大值,并进行累加
while (p <= q) {
leftMax = Math.max(leftMax, height[p]);
rightMax = Math.max(rightMax, height[q]);
sum += leftMax <= rightMax ? leftMax - height[p++] : rightMax - height[q--];
}
return sum;
}
}