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

77 阅读2分钟

当青训营遇上码上掘金

题目描述

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

725ef710a59944d49d0315bece7a1ac1_tplv-k3u1fbpfcp-zoom-in-crop-mark_4536_0_0_0.png

以下为上图例子的解析:

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

解题思路

  • 对于每个位置,向两边扩展,若能找到比它的高即可攒青豆,攒青豆的多少为两边较小的高度减去当前高度。要求取总攒下的青豆,遍历每个位置,对每个位置能攒青豆数求和即为总攒青豆数。
  • 对于各个位置,在遍历过程中,更新最大的高度,如果遇到比最高小的,那么代表可以攒青豆,否则更新最大高度,选择的是两侧中最值较小的进行遍历。

解题步骤

  1. 确定循环不变量
  2. 在满足循环不变量的情况下,根据左右指针指向的值更新当前位置的左右最大值
  3. 如果左指针指向的值小于右指针指向的值,则左边最大值小于右边最大值,计算左侧当前位置可攒青豆数,为当前左侧指针指向值和左侧最大值差的绝对值,加入总青豆数中,左指针右移。
  4. 如果左指针指向的值大于右指针指向的值,则左边最大值大于右边最大值,计算右侧当前位置可攒青豆数,为当前右侧指针指向值和右侧最大值差的绝对值,加入总青豆数中,右指针左移。
  5. 直到左右指针相遇。

实现代码

/**
* 支持 import Java 标准库 (JDK 1.8)
*/
import java.util.*;

/**
* 注意:目前 Java 代码的入口类名称必须为 Main(大小写敏感)
*青训营 X 码上掘金主题4攒青豆--双指针解法
*/
public class Main {
   public static void main(String []args) {
        int[] height = { 5, 0, 2, 1, 4, 0, 1, 0, 3};
        int total = SaveBean(height);
        System.out.println(total);
   }

  public static int SaveBean(int[] height) {
      int total = 0;
      int left = 0;
      int right = height.length - 1;
      int leftMax = 0;
      int rightMax = 0;
      
      //循环不变量:left < right
      while (left < right) {
        //左指针右移,右指针左移,更新当前leftMax和rightMax
          leftMax = Math.max(leftMax, height[left]);
          rightMax = Math.max(rightMax, height[right]);
          if (height[left] < height[right]) {
            //height[left] < height[right],则必有leftMax < rightMax
              total = total + leftMax - height[left];
              left++;
          } else {
            //height[left] > height[right],则必有leftMax > rightMax
              total = total + rightMax - height[right];
              right--;
          }
      }
      return total;
  }
}

代码已发布:码上掘金攒青豆代码链接