指针移动方向:相向夹逼,同向移动
维护的是一个区间还是只是关心指针指向的两个元素
同向移动的、维护一个区间的双指针法即滑动窗口法,
2Sum
排序后两头往中间夹逼的双指针法。指针为什么可以不回退?即为什么可以i只++, j只--?
当A[i]+A[j]<sum的时候,为什么只需要考查i++,而不用j++?因为j是从j+1来的,与A[j+1]对应的左边元素A[k] (k<=i) 比A[i]小,结果都超了sum,才到的A[j],所以A[j+1]加上现在的A[i]必然超sum,故而不用考虑。
2MinusCloset: 寻找两个数,其差值最接近給定值target
排序后,同向移动的双指针法。
当A[j]-A[i]>target 时候,为什么只需要i++, 而不需要j--? 因为j-1的时候对应的A[i]比现在的A[i]要小,结果小于target才j++,如果用A[j-1]对应现在的A[i]差值会更小,故而不用再看了
int towMinusClosest(vector<int> &A, int target) {
assert(A.size() > 1);
int i = 0, j = 1, closest = A[1] - A[0];
for (; j < A.size();) {
if (abs(A[j] - A[i] - target) < abs(closest - target))
closest = A[j] - A[i];
if (closest == target) break;
if (A[j] - A[i] > target) {
++i;
if (i == j) ++j;
}
else ++j;
}
return closest;
}
subarray sum (all positive) 不需要有序
滑动窗口法。
可以这样理解:枚举起点为i的子数组,j不断往后推,当超过sum的时候就没必要继续枚举了,起点为i的子数组枚举结束,枚举起点为i+1的子数组,这里重点是,j不用回退,因为A[i]+..+A[j-1] < sum,则A[i+1]+...+A[j-1] 少了一个正数A[i]必然也小于 sum,所以j不用回退。
def f(a, t):
l, r, curSum = 0, 0, a[0]
while r < len(a):
if curSum == t:
print a[l : r + 1]
r += 1
if r < len(a): curSum += a[r]
elif curSum > t:
curSum -= a[l]
l += 1
else:
r += 1
if r < len(a): curSum += a[r]
对于排序数组的2sum,有一个扩展:两个数组A,B,要求2sum的两个数从A、B中各取一个
解法:两遍双指针夹逼法,1)A[0] vs B[n -1],2)A[n-1] vs B[0]