Array题型套路--双指针

套路

细分为同向双指针和反向双指针两种。

同向双指针模板

Initialize two pointers i and j, usually both equal to 0
while j < array.length:
  if we need array[j], then 
 	 array[i] = array[j];
     i++;
  else 
  	skip it. We do not need to move i since its spot is not fulfilled
  j++;

反向双指针模板

Initialize two pointers i = 0, j = array.length – 1
while i <= j:
  Decide what you should do based on the value of array[i] and array[j]
  Move at least one pointer forward in its direction

应用

26. Remove Duplicates from Sorted Array

Container With Most Water (11)

42. Trapping Rain Water

这个题目最好理解的思路有两个,一个是DP,一个是对撞指针(反向双指针)。
共同的思路,都是使用一个全局变量记录雨水数量,初始为0,然后考虑每一个柱子,找到它左边(可以包括当前)最高以及右边最高(可以包括当前柱子)的柱子,那么它能接的雨水,就是左右最大高度的最小值-当前柱子的高度。

思路1:DP,使用两个数组left[i], right[i]分别记录从两边到达 h[i]的最大柱子高度。使用两次循环遍历即可。最后,再遍历每一个柱子,计算雨水量。

public int trap(int[] A) { // time: O(n), space: O(n)
    int n = A.length;
    int[] left = new int[n];
    int[] right = new int[n];

    left[0] = A[0]; // 计算每个A[i]左边最高的柱子
    for(int i = 1; i < n; i++) {
        left[i] = Math.max(left[i-1], A[i]);
    }

    right[n - 1] = A[n-1]; // 计算每个A[i]右边最高的柱子
    for(int i = n - 2; i >= 0; i--) {
        right[i] = Math.max(right[i+1], A[i]);
    }

    int ans = 0;   // 统计每个柱子能接到的雨水
    for(int i = 0; i < n; i++) {
        ans += Math.min(left[i], right[i]) - A[i];
    }
    return ans;
}

思路2: 双指针,使用left, right对撞指针,使用leftMax, rightMax记录两个指针在遍历过程中的最大值(包括当前指针自身)。
如果A[left] < A[right] 则移动left (++), 否则移动right (--),通过这种策略,可以知道每个柱子左右的最大值。
如果 A[left] < A[right],那么一定有 leftMax < rightMax(①),因此left所在的柱子能接收的雨水,就是 leftMax - A[left].
难点:如何证明①,虽然直觉上一定是对的。我们可以采用反证法,如果leftMax是在left的左边,且leftMax > rightMax的话,
那么left就不应该移动,而是right会一直往左边移动,越过right,这跟当前的这个情形矛盾(left > index(leftMax))

public int trap(int[] A) {
    int left = 0, right = A.length - 1;
    int leftMax = 0, rightMax = 0;
    int ans = 0;
    while (left < right) {
        leftMax = Math.max(leftMax, A[left]); // 更新左边的最大值(包括left位置)
        rightMax = Math.max(rightMax, A[right]); // 更新右边的最大值 (包括right位置)
        if (A[left] < A[right]) { // must have leftMax < rightMax
            ans += leftMax - A[left++];
        } else {
            ans += rightMax - A[right--];
        }
    }
    return ans;
}
posted @ 2023-05-21 10:02  编程爱好者-java  阅读(15)  评论(0编辑  收藏  举报