16. 最接近的三数之和
- 双指针+减去多余步骤
- 测试代码
- 测试结果
给你一个长度为 n 的整数数组 nums 和 一个目标值 target。请你从 nums 中选出三个整数,使它们的和与 target 最接近。
返回这三个数的和。
假定每组输入只存在恰好一个解。
示例 1:
输入:nums = [-1,2,1,-4], target = 1
输出:2
解释:与 target 最接近的和是 2 (-1 + 2 + 1 = 2) 。
示例 2:
输入:nums = [0,0,0], target = 1
输出:0
提示:
3 <= nums.length <= 1000
-1000 <= nums[i] <= 1000
-104 <= target <= 104
双指针+减去多余步骤
当我们在遍历的时候,三数之和为,sum = nums[i]+ numsli]+ nums[k],判断sum是不是与target最近的数。
使用双指针的方法:
我们要先对数组进行排序,使得它是有序的,在使用双指针时,更好的来控制哪一个指针移动。
我们需要分类讨论:
1.如果sum = target,直接返回sum。
2.如果三数之和与target的距离比ans与target的距离更近,我们将sum赋值给ans。
if (Math.abs(sum-target)<Math.abs(ans-target))
ans=sum;
3.如果j<k,判断sum的值是大于还是小于target,如果是小于,因为是排了序的,所以我们使得j++,使得sum的更接近target。如果是大于,因为是排了序的,所以我们使得k- -,使得sum的更接近target。
减去一些多余的步骤
1.设sum = nums[i]+ numsi+1]+ nums[i+2]。此时sum > target,由于数组已排序,表示了在往后面走只会使得sum变大,离target越来越远。所以这种时候可以直接break外层循环了。不过还是要判断sum是否比ans离target更近。
2.设sum = nums[i]+ nums[n-2]+ nums[n -1]。此时sum < target,由于数组已排序,表示了nums[i]加上后面任意两个数都不超过sum,在往前面走只会使得sum变大,离target越来越远。此时不应该break,而是continue,因为nums[i],后面的数字可能存在加上nums[n-2]+ nums[n -1],sum更大的情况。在之前要判断sum是否比ans离target更近。
3.当i >0并且nums[i]= nums[i-1],那么此时nums[i]和后面数字相加的情况,在之前为nums[i-1]时候就已经算过了,直接continue外层循环。
测试代码
class Solution {
public int threeSumClosest(int[] nums, int target) {
Arrays.sort(nums);
int n=nums.length;
int sum=0,ans=1<<30;
for (int i = 0; i <n-2; i++) {
//后面有相同数字时直接跳过
if (i>0&&nums[i]==nums[i-1]){
continue;
}
//加的两个数字在往后走,三数之和,只会离target越来越远
sum=nums[i]+nums[i+1]+nums[i+2];
if (sum>target){
if ((sum-target)<Math.abs(ans-target))
ans=sum;
break;
}
//加的两个数字在往前走,三数之和,只会离target越来越远
sum=nums[i]+nums[n-2]+nums[n-1];
if ((sum)<target){
if ((target-sum)<Math.abs(ans-target))
ans=sum;
continue;
}
int k=n-1,j=i+1;
while (j<k){
sum=nums[i]+nums[j]+nums[k];
if (sum==target)return sum;
if (Math.abs(sum-target)<Math.abs(ans-target))
ans=sum;
if (sum<target) j++;
else k--;
}
}
return ans;
}
}
测试结果
{:width=400}