「青训营 X 码上掘金」主题 4:攒青豆

116 阅读2分钟

当青训营遇上码上掘金

注:用的java,三重for循环实现,没用到指针,只是谈一个思路,并没涉及什么算法

介绍

现有 n 个宽度为 1 的柱子,给出 n 个非负整数依次表示柱子的高度,排列后如下图所示,此时均匀从上空向下撒青豆,计算按此排列的柱子能接住多少青豆。(不考虑边角堆积)

以下为上图例子的解析:

输入:height = [5,0,2,1,4,0,1,0,3]  
输出:17  
解析:上面是由数组 [5,0,2,1,4,0,1,0,3] 表示的柱子高度,在这种情况下,可以接 17 个单位的青豆。

思路

我的想法是就像水杯装水一样,得两侧都有杯壁水才不会漏

同理,如果将这个想象成一个一个的小格子,然后每个青豆格的左右延伸出去必定有柱子格(想象不出可以看下图)

image.png

那我们就可以这样做:

  1. 先找到所给数组的最大值以及数组长度
  2. 横向循环遍历,循环内部竖向遍历,竖向遍历再内部横向遍历(原理见下图)

image.png

  1. 每次遍历到有青豆的格子就直接开始遍历下一列,因为是从上往下遍历,所以只要上面有青豆就说明这一列的下面都有青豆
  2. 最后遍历结束就可以计算总共的青豆数量了

实现

  1. 找到所给数组的最大值以及数组长度
int[] height = new int[]{5, 0, 2, 1, 4, 0, 1, 0, 3};    // 定义高度
int maxHeight = Arrays.stream(height).max().getAsInt();    // 数组的最大值
int maxWidth = height.length;    // 数组长度
int beans = 0;    // 青豆数量
  1. 横向遍历
for (int x = 0; x < maxWidth; x++) {
    // 高度为1或2时直接结束,青豆数量为0
    if (maxWidth < 3) {
        break;
    }
    // 排除第一列和最后一列
    if (x == 0 || x == maxWidth - 1) {
        continue;
    }

    /* 此处为竖向从上往下遍历的代码 */

}
  1. 竖向遍历,内部横向遍历
for (int y = maxHeight; y > height[x]; y--) {
    boolean left = false; // 用于保留左边对比的结果
    // 横向遍历
    for (int x2 = 0; x2 < maxWidth; x2++) {
        // 左边对比
        if (x2 < x) {
            if (height[x2] >= y) {
                left = true;
                x2 = x; // 使x2=x,使for循环x2++后可以跳过后续左边多余的对比
            }
            continue;
        }
        // 等于
        if (x2 == x) continue;
        // 右边,只有 x2>x 时才会运行到这里
        if (left && height[x2] >= y) {
            // 因为是从上往下遍历,所以上面有青豆说明这一列下面也会有,数量为 y-height[x]
            beans += y - height[x]; // 加上这一列的青豆数量
            y = height[x];  // 加好之后,这一列不需要竖向继续往下遍历了
            break;
        }
    }
}
  1. 最后输出青豆数量
System.out.println(beans);  // 打印青豆数量

完整代码: 攒青豆 - 码上掘金 (juejin.cn)