双指针:两数之和
只是记录自己的刷题过程,答案参考过多种题解。
如有错误感谢指正!
参考:LeetCode 101: A LeetCode Grinding Guide (C++ Version) 作者:高畅 Chang Gao
A. 双指针思想
-
因为数组已经排好序,我们可以采用 方向相反的双指针 来寻找这两个数字。
B. 具体实现
-
两个指针,一个初始指向最小的元素,即数组最左边,向右遍历;一个初始指向最大的元素,即数组最右边,向左遍历。
-
如果两个指针指向元素的和 等于 给定值,那么它们就是我们要的结果;如果两个指针指向元素的和 小于 给定值,我们把左边的指针右移一位,使得当前的和增加一点;如果两个指针指向元素的和 大于 给定值,我们把右边的指针左移一位,使得当前的和减少一点。
C. 思考
-
可以证明,对于 排好序且有解 的数组,双指针一定能遍历到最优解。
-
证明方法如下:假设最优解的两个数的位置分别是 l 和 r。我们假设在左指针在 l 左边的时候,右指针已经移动到了 r;此时两个指针指向值的和小于给定值,因此左指针会一直右移直到到达 l。同理,如果我们假设 在右指针在 r 右边的时候,左指针已经移动到了 l;此时两个指针指向值的和大于给定值,因此右指针会一直左移直到到达 r。所以双指针在任何时候都不可能处于 (l,r) 之间,又因为不满足条件时指针必须移动一个,所以最终一定会收敛在 l 和 r。
-
如果数组没有排序,且题目要求和数组顺序无关,可以考虑采取先排序后双指针的做法。
-
从 缩减搜索空间 的角度思考。无论
A[i] + A[j]
的结果是大了还是小了,我们都可以排除掉一行或者一列的搜索空间。经过 n 步以后,就能排除所有的搜索空间,检查完所有的可能性。
D. 代码
public int[] twoSum(int[] numbers, int target) {
int start = 0, end = numbers.length - 1, sum;
while (start < end) {
sum = numbers[start] + numbers[end];
if (sum == target) {
return new int[]{start + 1, end + 1}; //按要求返回新数组
} else if (sum < target) { // 结果小于给定值,则开始的指针下标自增
++start;
} else { // 结果大于给定值,则结束的指针下标自减
--end;
}
}
return new int[]{-1, -1};
}