双指针算法
- 双指针主要用于遍历数组,两个指针指向不同的元素,从而协同完成任务。也可以延伸到多个数组的多个指针。
- 若两个指针指向同一数组,遍历方向相同且不会相交,则也称为滑动窗口(两个指针包围的区域即为当前的窗口),经常用于区间搜索。
- 若两个指针指向同一数组,但是遍历方向相反,则可以用来进行搜索,待搜索的数组往往是排好序的。
- 数组或字符串中,一般为左右指针,链表中一般为快慢指针。
例题
167. 两数之和 II - 输入有序数组
题解
- 因为数组已经排好序,我们可以采用方向相反的双指针来寻找这两个数字,一个初始指向最 小的元素,即数组最左边,向右遍历;一个初始指向最大的元素,即数组最右边,向左遍历。
- 如果两个指针指向元素的和等于给定值,那么它们就是我们要的结果。如果两个指针指向元 素的和小于给定值,我们把左边的指针右移一位,使得当前的和增加一点。如果两个指针指向元 素的和大于给定值,我们把右边的指针左移一位,使得当前的和减少一点。
public int[] twoSum(int[] numbers, int target) {
int[] answer = new int[2];
int sum;
//左指针
int start = 0;
//右指针
int end = numbers.length-1;
//终止条件 start==end 此时已经测试完所有可能的结果
while(start<end){
sum=numbers[start]+numbers[end];
if(sum==target){
answer[0]=start+1;
answer[1]=end+1;
break;
}
if(sum<target) start++;
//==和>=都会走该句 但前面==后break 故只有>=会走
else end--;
}
return answer;
}
88. 合并两个有序数组
题解
-
因为这两个数组已经排好序,我们可以把两个指针分别放在两个数组的末尾,即 nums1 的 m − 1 位和 nums2 的 n − 1 位。每次将较大的那个数字复制到 nums1 的后边,然后向前移动一位。 因为我们也要定位 nums1 的末尾,所以我们还需要第三个指针,以便复制。
public void merge(int[] nums1, int m, int[] nums2, int n) { //直接利用m和n指向两个数组已有元素的末尾 pos指向nums1末尾 int pos = m-- +n-- -1; while(m>=0&&n>=0){ //nums2的数较大时移向nums1末尾 if(nums1[m]<=nums2[n]){ nums1[pos--]=nums2[n]; n--; }else { nums1[pos--]=nums1[m]; m--; } } //如果 nums1的数字已经复制完,不要忘记把 nums2 的数字继续复制 while(n>=0){ nums1[n]=nums2[n]; n--; } }
633. 平方数之和
题解
- 这是一道经典的双指针相向而行的题目,但是要注意剪枝,就是right部分一定小于等于c的平方根,所以首先要把right部分确定下来,左右双向而行就可,大了就right左移,小了就left右移,直到相遇或者平方和为c,代码如下:
- 起始条件:左指针 0 右指针 c的平方根 (如果能与两个数的平方和相等,两个数一定在该范围内,在这个范围内进行双指针搜索),左右指针分别指示着较小和较大的数
- 终止条件:左指针大于右指针 或 c与两个数的平方和相等
public boolean judgeSquareSum(int c) {
int middle = (int) Math.sqrt(c);
int left = 0;//左指针
int right = middle;//右指针
long sum;//避免结果溢出
while(right>=left){
sum=left*left+right*right;
if(sum==c) return true;
if(sum>c) right--;
else left++;
}
return false;
}