双指针法是一个可以是一个算法模板,经常被运用在有序的数组或列表中。和滑动窗口不同的是,此时的双指针是从两侧到中间,通俗点也可以称为夹逼法。例如,从一个有序数组中找到符合一定条件的元素组。
直接遍历整个集合,需要进行O(n^2)或者O(n^3)复杂度的遍历。而双指针的夹逼法,通常可以只遍历一次即可找到结果,有效的节省了时间复杂度和空间复杂度。
确定何时使用双指针方法的方法:
- 它将处理排序数组(或链表)并需要找到一组满足某些约束的元素的问题
数组中的元素集是一对、三元组,甚至是子数组。
https://leetcode.com/problems/two-sum-ii-input-array-is-sorted/
该题是最典型的双指针夹逼法去求解的题。和上图示意一致。直接用双指针,从头和尾往中间查找。代码如下:
public int[] twoSum(int[] numbers, int target) {
int left = 0, right = numbers.length - 1;
int[] res = new int[2];
while(left <= right) {
if (numbers[left] + numbers[right] == target) {
res[0] = left + 1;
res[1] = right + 1;
break;
} else if (numbers[left] + numbers[right] < target) {
left++;
} else {
right--;
}
}
return res;
}
求解的效果是很不错的:
https://leetcode.com/problems/squares-of-a-sorted-array/
这个也没啥好说的,因为是有序,但是存在负数,所以平方后的排序一定是头和尾的绝对值大小,大的平方也大,这样可以确定最后保存的位置。
public int[] sortedSquares(int[] nums) {
// 采用两个指针,一个开头,一个结尾
// 对比两个指针的squaring值的大小,从最后一个元素开始,大的元素放到最后
// 如果是右侧值大,则right--,左侧大,则left++;
int size = nums.length;
int left = 0, right = size - 1;
int[] res = new int[size];
while (left <= right) {
if (Math.abs(nums[right]) > Math.abs(nums[left])) {
res[--size] = nums[right] * nums[right];
right--;
} else {
res[--size] = nums[left] * nums[left];
left++;
}
}
return res;
}
另外还有3 sum : https://leetcode.com/problems/3sum/
Trapping Rain Water : https://leetcode.com/problems/trapping-rain-water/
等一些题可以采用该方法解决。
双指针法广泛的用于滑动窗口,二分查找,链表遍历(快慢指针)和本文提到的夹逼法,而它们的不同在于指针移动的顺序不同,可以多对比学习。