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;
}