两数之和
class Solution {
public int[] twoSum(int[] numbers, int target) {
/* 解题思路:相向双指针
* 令指针left和指针right分别指向当前最小和最大的两个数
* 初始化:left = 0, right = n-1
* 在每一轮循环中:
计算sum = nums[left] + nums[right]
(假设mid是nums[left]和nums[right]之间的任意数)
判断sum ? target:
sum == target: 找到解,return [left, right]
sum > target: nums[right]+mid必然大于target,不必再判断nums[right],right--;
sum < target: nums[left]+mid必然小于target, 不必再判断nums[left],left++;
* 退出循环的条件:while(left < right) loop;
*/
// 初始化指针
int left = 0;
int right = numbers.length - 1;
// 循环移动双指针
while(left < right){
int sum = numbers[left] + numbers[right];
if(sum == target){
// 下标从1开始
return new int[]{left+1, right+1};
}else if(sum > target){
right--;
}else{
left++;
}
}
return null;
}
}
三数之和
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
/* 实现思路:基本思路就是先排序,然后枚举i,在每一个i下转换为对j和k的两数之和问题
* 如何避免重复解?
* 1. 规定 i <= j <= k,即 nums[i] <= nums[j] <= nums[k]
* 2. 当需要移动指针时,要移动到下一个不重复的数字
*/
Arrays.sort(nums); // 对数组进行排序
List<List<Integer>> ans = new ArrayList<>();
int n = nums.length;
int i = 0;
while (i < n - 2) {
int x = nums[i];
// 优化一:如果「在当前i下sum能取到的最大值」小于0,说明当前i下不会有解了,跳过当前i
if(x + nums[n - 2] + nums[n - 1] < 0) {
i = moveI(nums, i);
continue;
}
// 优化二:如果「sum能取到的最小值」大于0,说明之后不会有解了,直接结束循环
if(x + nums[i + 1] + nums[i + 2] > 0){
break;
}
// 在当前i下,将三数之和问题转换为对j和k的两数之和问题
int j = i + 1, k = n - 1;
while (j < k) {
int s = x + nums[j] + nums[k];
if (s > 0) k = moveK(nums, j, k);
else if (s < 0) j = moveJ(nums, j, k);
else {
ans.add(List.of(x, nums[j], nums[k]));
j = moveJ(nums, j, k);
k = moveK(nums, j, k);
}
}
i = moveI(nums, i);
}
return ans;
}
// 将指针I移动到下一个不重复的数字上
private int moveI(int[] nums, int i){
i++;
while(i < nums.length - 2 && nums[i] == nums[i - 1]){
i++;
}
return i;
}
// 将指针j移动到下一个不重复的数字上
private int moveJ(int[] nums, int j, int k){
j++;
while(j < k && nums[j] == nums[j - 1]){
j++;
}
return j;
}
// 将指针k移动到下一个不重复的数字上
private int moveK(int[] nums, int j, int k){
k--;
while(k > j && nums[k] == nums[k + 1]){
k--;
}
return k;
}
}