「青训营 X 码上掘金」接雨水变形(攒青豆)

78 阅读3分钟

当青训营遇上码上掘金

题目介绍

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

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

题目解法

本题的解法其实多种多样,一般主流解法为动态规划,暴力,单调栈以及双指针法。在这道题中,博主的解法主要是双指针方法,控制时间复杂度在O(n)内,空间复杂度O(1)。

在这个题目中,我们使用双指针来遍历数组,只考虑当前有可能的最优解。

我们可以使用两个指针,一个指针从左向右扫描,另一个指针从右向左扫描。在左指针扫描时,如果遇到高度小于等于左指针所指高度,则相互做差更新青豆数,如果遇到高度大于左指针所指高度,则继续上述行为,相互做差更新青豆数。在右指针扫描时,如果遇到高度小于等于右指针所指高度,则相互做差更新青豆数,如果遇到高度大于右指针所指高度,则青豆数此次更新为0。然后我们计算当前所有可能的存储量,并更新答案。将两个指针中各移动一位,继续扫描。这样我们就能有效减少比较次数和提高效率。

public class Main {

  public static int saveGreenBeans(int[] height) {
    if (height == null || height.length <= 2) {
      return 0;
    }
    int count = 0;
    int N = height.length;
    int leftMax = height[0];
    int rightMax = height[N - 1];
    int L = 1;
    int R = N - 2;
    while(L <= R) {
      if (leftMax <= rightMax) {
        count += Math.max(0, leftMax - height[L]);
        leftMax = Math.max(leftMax, height[L++]);
      } else {
        count += Math.max(0, rightMax - height[R]);
        rightMax = Math.max(rightMax, height[R--]);
      }
    }
    return count;
  }

   public static void main(String []args) {
      int[] height = new int[]{5,0,2,1,4,0,1,0,3};
      int count = saveGreenBeans(height);
      System.out.println(count);
   }
}

这段代码中,我们首先初始化左右指针 L 和R。然后,我们使用两个变量 leftMax 和 rightMax 记录当前数组中的最大值(初始值leftMax为height[0],rightMax为height[N - 1]),并使用一个变量 count 来记录青豆的最大数量。

在 while 循环中,我们比较两个指针所指向的位置的高度,如果左指针所指向的高度小于等于右指针所指向的高度,则说明左指针所指向的位置是当前左右最大值最小的,我们将 leftMax - 当前遍历到数组值如果大于0我们就更新青豆数否则青豆数加0,同时比较当前值和leftMax继而更新leftMax然后左指针加加。否则,右指针所指向的位置是当前左右最大值最小的,我们将 rightMax - 当前遍历到数组值如果大于0我们就更新青豆数否则青豆数加0,同时比较当前值和rightMax继而更新rightMax然后右指针加加。最后,我们移动左右指针直到两个指针错过。 这道题双指针的时间复杂度是O(n)。因为每个位置被访问和处理最多一次,所以总的时间复杂度为O(n)。空间复杂度是O(1)

因为博主平常写文章很少,所以有不足还望大家指出!!!