双指针法解决攒青豆问题

88 阅读2分钟

先上代码

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

/**
* 注意:目前 Java 代码的入口类名称必须为 Main(大小写敏感)
*/
public class Main {
    public static void main(String[] args) {
        int[] height = {5, 0, 2, 1, 4, 0, 1, 0, 3};
        //int[] height = {0, 3,0,2,5,4};
        int beans = gatherGreenBeans(height, 0, height.length - 1);
        System.out.println(beans);
    }

    private static int gatherGreenBeans(int[] height, int left, int right) {
        //左边最高柱子和右边最高柱子
        int leftHighest = 0, rightHighest = 0;
        //可接住的青豆数
        int beans = 0;
        //从左边或者右边找到非零的最低的那颗柱子并进行相应计算
        while (left < right) {
            if (height[left] > height[right]) {
                rightHighest = Math.max(height[right], rightHighest);
                beans += rightHighest - height[right];
                right--;
            } else {
                leftHighest = Math.max(height[left], leftHighest);
                beans += leftHighest - height[left];
                left++;
            }
        }
        return beans;
    }
}
  1. 观察问题
    看到攒青豆问题,我首先想到的是可以攒青豆的有哪些地方?根据观察,我们可以发现,当柱子之间有空隙时就可以积攒青豆,具体的,当一个柱子的高度小于它左右两边的高度时,就有可能在这个柱子上面攒青豆,例如有[2,1,2]的柱子,这时我们可以在高度为1的柱子上面攒青豆
  2. 如何实现
    我们需要先找到这些可以积攒青豆的柱子,进行相应计算得到青豆的数量.在这里,我用到的是双指针法解决攒青豆的问题.
  3. 具体实现
  • 具体的,我们可以定义两个指针leftright,一个指向数组的左端点,一个指向数组的右端点,逐步向中间遍历找到可以积攒豆子的柱子
  • 每次比较leftright指针所指向柱子的高度,如果左边的柱子高度大于右边柱子高度,则我们可以从右向左找可以积攒豆子的柱子,我们用rightHighest代表已经遍历的右边柱子的最高高度,并通过比较right所指向柱子的高度和rightHighest的值更新rightHighest的值,在更新完rightHighest之后,如果right所指向柱子的高度小于rightHighest,则该柱子可以积攒青豆rightHighest-height[right]个,将这个数加到beans上并将右指针向左移动.
  • 类似的,如果右边柱子高度小于左边柱子高度,则我们可以从左向右找可以积攒豆子的柱子,我们用leftHighest代表以及遍历的左边柱子的最高高度,并通过比较left所指向柱子的高度和leftHighest的值更新leftHighest的值,在更新完leftHighest之后,如果left所指向柱子的高度小于leftHighest,则该柱子可以积攒青豆leftHighest-height[left]个,将这个数加到beans上并将右指针向右移动.
  • 不断重复上述步骤,直至left>right,此时,beans的数值即为我们所求的青豆数.