左右指针主要解决数组(或者字符串)中的问题,比如二分查找。
一、二分法查找
1、二分查找的框架
int binarySearch(int[] nums, int target) {
int left = 0, right = ...;
while (...){
int mid = (right + left) / 2;
if (nums[mid] == target) {
...
} else if (nums[mid] < target) {
left = ...
} else if (nums[mid] > target) {
right = ...
}
}
return ...;
}
分析二分查找的一个技巧是:不要出现 else,而是把所有情况用 else if 写清楚,这样可以清楚地展现所有细节。本文都会使用 else if,旨在讲清楚,读者理解后可自行简化。其中...标记的部分,就是可能出现细节问题的地方,当你见到一个二分查找的代码时,首先注意这几个地方。后文用实例分析这些地方能有什么样的变化。
另外声明一下,计算 mid 时需要技巧防止溢出,建议写成: mid = left + (right - left) / 2,本文暂时忽略这个问题。
2、寻找一个数(基本的二分搜索)
这个场景是最简单的,可能也是大家最熟悉的,即搜索一个数,如果存在,返回其索引,否则返回其在数组中插入的位置。
public static int binarySearch(int[] array, int target) {
int start = 0;
int end = array.length - 1; //注意
int mid = 0;
while (start <= end) {//注意<=
mid = (start + end) >> 1;
if (array[mid] == target) {
return mid;
} else if (array[mid] < target) {
start = mid + 1;//注意
} else if (array[mid] > target) {
end = mid - 1;//注意
}
}
return end + 1;
}
对二分法进一步的思考参考:www.cnblogs.com/kyoner/p/11…
二、 数组求和
链接:leetcode-cn.com/problems/tw…
给定一个已按照升序排列 的有序数组,找到两个数使得它们相加之和等于目标数。函数应该返回这两个下标值 index1 和 index2,其中index1 必须小于 index2。
说明:
返回的下标值(index1 和 index2)不是从零开始的。你可以假设每个输入只对应唯一的答案,而且你不可以重复使用相同的元素。
示例:
输入: numbers = [2, 7, 11, 15], target = 9
输出: [1,2]
解释: 2 与 7 之和等于目标数 9 。因此 index1 = 1, index2 = 2 。
思路:
设置两个左右指针,一个纸箱头部,一致指向尾部,对着两个指针指向的数求和,如果和大于目标数,则将左指针右移,如果和小于目标数,则说明右指针需要右移,若两个指针相遇,即left==right都没有找到这两个数的话,则不存在两个数之和等于target
代码:
public static int[] position(int[] array, int target) {
int left = 0;
int right = array.length - 1;
while (left < right){
if (array[right] + array[left] == target){
break;
}else if (array[right] + array[left] < target){
left++;//如果和大于目标数,则将左指针右移
}else {
right--;//如果和小于目标数,则说明右指针需要右移
}
return new int[]{++left, ++right};
}
2、三数之和
链接:leetcode-cn.com/problems/3s…
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。
注意:答案中不可以包含重复的三元组。
示例 1:
输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]
三数之和可以看成两数之和的高阶版本,只是有一些重复的情况需要额外处理。先对数组进行排序,然后遍历,那么,实际上三数之和就是在剩下的数组里面再找两个数的和为0-nums[i]
public List<List<Integer>> threeSum(int[] nums) {
Arrays.sort(nums);
int length = nums.length;
List<List<Integer>> res = new ArrayList<>();
for(int k = 0; k < length - 2; k++){
//最小的数都大于0,直接结束
if(nums[k] > 0)
break;
if(k > 0 && nums[k] == nums[k - 1])
continue;
int i = k + 1;
int j = length - 1;
while(i < j){
int sum = nums[k] + nums[i] + nums[j];
if(sum < 0){
while(i < j && nums[i] == nums[i + 1]){
++i;
};
++i;
} else if (sum > 0) {
while(i < j && nums[j] == nums[i + 1]){
--j;
};
--j;
} else {
res.add(new ArrayList<>(Arrays.asList(nums[k], nums[i], nums[j])));
while(i < j && nums[i] == nums[i + 1]){
++i;
};
++i;
while(i < j && nums[j] == nums[j - 1]){
--j;
};
--j;
}
}
}
return res;
}
}
注意每次left++或者right--之后都有可能导致数组越界或者left越过right的可能性,所以每次都要判断left < right是否成立
3、反转数组
public static void reverse(int[] nums) {
int tmp = 0;
int left = 0;
int right = nums.length - 1;
while (left < right){
tmp = nums[left];
nums[left++] = nums[right];
nums[right--] = tmp;
}
}
数组是引用变量,所以并不需要有返回类型,只需要对当前数组进行翻转,函数外部的数组也会跟着翻转 【参考】 【1】leetcoe 【2】www.cnblogs.com/kyoner/p/11…
本文已参与「新人创作礼」活动, 一起开启掘金创作之路。