当青训营遇上码上掘金之“攒青豆”
题目:攒青豆
现有n个宽度为1的柱子,给出n个非负整数依次表示柱子的高度,排列后如下图所示,此时均匀从上空向下撒青豆,计算按此排列的柱子能接住多少青豆。(不考虑边角堆积)
以下为上图例子的解析:
输入:height = [5,0,2,1,4,0,1,0,3]
输出:17
解析:上面是由数组 [5,0,2,1,4,0,1,0,3] 表示的柱子高度,在这种情况下,可以接 17 个单位的青豆。
解题思路
黑色的看成墙,蓝色的看成豆子,宽度一样,给定一个数组,每个数代表从左到右墙的高度,求出能装多少单位的豆子。也就是图中蓝色正方形的个数。
解法一:按行求
这是我最开始想到的一个解法,就是先求高度为1的豆子,再求高度为2的豆子,再求高度为3的豆子。
整个思路就是,求第i层的豆子,遍历每个位置,如果当前的高度小于i,并且两边有高度大于等于i的,说明这个地方一定有豆子,豆子就可以加1。
如果求高度为i的豆子,首先用一个变量temp保存当前累积的豆子,初始化为0。从左到右遍历墙的高度,遇到高度大于等于i的时候,开始更新temp。更新原则是遇到高度小于i的就把temp加1,遇到高度大于等于i的,就把temp加到最终的答案ans里,并且temp置零,然后继续循环。
代码实现:
public int trap(int[] height) {
int sum = 0;
int max = getMax(height);//找到最大的高度,以便遍历。
for (int i = 1; i <= max; i++) {
boolean isStart = false; //标记是否开始更新 temp
int temp_sum = 0;
for (int j = 0; j < height.length; j++) {
if (isStart && height[j] < i) {
temp_sum++;
}
if (height[j] >= i) {
sum = sum + temp_sum;
temp_sum = 0;
isStart = true;
}
}
}
return sum;
}
private int getMax(int[] height) {
int max = 0;
for (int i = 0; i < height.length; i++) {
if (height[i] > max) {
max = height[i];
}
}
return max;
}
解法二:按列求
求每一列的豆子,我们只需要关注当前列,以及左边最高的墙,右边最高的墙就够了。
装豆子的多少,当然根据木桶效应,我们只需要看左边最高的墙和右边最高的墙中较矮的一个就够了。
所以,根据较矮的那个墙和当前列的墙的高度可以分为三种情况。
- 较矮的墙的高度大于当前列的墙的高度
把正在求的列左边最高的墙和右边最高的墙确定后,然后为了方便理解,我们把无关的墙去掉。
这样就很清楚了,现在想象一下,往两边最高的墙之间撒豆子。正在求的列会有多少豆子?
很明显,较矮的一边,也就是左边的墙的高度,减去当前列的高度就可以了,也就是 2 - 1 = 1,可以存一个单位的豆子。
- 较矮的墙的高度小于当前列的墙的高度
同样的,我们把其他无关的列去掉。
想象下,往两边最高的墙之间撒豆子。正在求的列会有多少豆子?
正在求的列不会有豆子,因为它大于了两边较矮的墙。
- 较矮的墙的高度等于当前列的墙的高度。
和上一种情况是一样的,不会有豆子。
明白了这三种情况,程序就很好写了,遍历每一列,然后分别求出这一列两边最高的墙。找出较矮的一端,和当前列的高度比较,结果就是上边的三种情况。
代码实现:
public int trap(int[] height) {
int sum = 0;
//最两端的列不用考虑,因为一定不会有豆子。所以下标从 1 到 length - 2
for (int i = 1; i < height.length - 1; i++) {
int max_left = 0;
//找出左边最高
for (int j = i - 1; j >= 0; j--) {
if (height[j] > max_left) {
max_left = height[j];
}
}
int max_right = 0;
//找出右边最高
for (int j = i + 1; j < height.length; j++) {
if (height[j] > max_right) {
max_right = height[j];
}
}
//找出两端较小的
int min = Math.min(max_left, max_right);
//只有较小的一段大于当前列的高度才会有豆子,其他情况不会有豆子
if (min > height[i]) {
sum = sum + (min - height[i]);
}
}
return sum;
}
结果展示
测试代码:
public static void main(String []args) {
int[] height = {5,0,2,1,4,0,1,0,3};
int res=trap(height);
System.out.print("最后接住的青豆是:");
System.out.print(res);
}
输出结果:
总结
完整代码链接:青训营4-攒青豆 - 码上掘金 (juejin.cn)
笨人纯小白!!如果有其他的思路或者更好的解法,亦或者发现了文章出现了错误或有不足,欢迎在评论区批评指正!