
我的思路
这个题难度不大,参考这个📄((20250730120638-7fmtj3w ‘0015 三数和’)),((20250728094651-r58pb04 ‘0001 两数之和’)) ,((20250730131645-jdjxatg ‘0167 两数之和II-输入有序数组’))
这个题相比之前的区别在于这个是和与target最接近。
难点在于我们应该什么时候移动这个j和k
- 先排序,排序之后,就可以更方便的使用双指针了
- 先用y来表示三者的和与target的插值
如果为0代表这个就是最接近的,可以直接返回了
如果大于0,代表这个target比较大,所以我们要让这三者的和增大一点,移动左边的指针
如果小于0,代表这个target比较小,所以我们要让这三者的和减少一点,移动右边的指针
int n = nums.length;
Arrays.sort(nums);
int min = 100001;
int ans = 0;
for(int i = 0;i<n-2;i++){
//确定的第一个数
int x = nums[i];
int j = i+1;
int k = n-1;
while(j<k){
int y = target - x - nums[j] - nums[k];
if(y==0){
return target;
}else if(y>0){
j++;
}else {
k--;
}
int temp = Math.abs(y);
if(temp<min){
min = temp;//取最小值
ans = target - y;
}
}
}
return ans;
灵神的思路
灵神和我主体思路是相同的,区别在于他想到了很多的优化方案
对于这个算法,我们已经能够遍历出所有的情况,但是有些是没有必要的,可以直接排除,这个就是剪枝
优化1:去重
如果i>0,并且nums[i]==nums[i-1]代表这个数和前一个数相同,那么这个数就不用再次便利了
if (i > 0 && x == nums[i - 1]) {
ontinue; // 优化1
}
优化2:排除极限情况
如果排序后的数组,最小的三个数和仍然大于target,就代表,后续的所有情况都会大于target,我们取最小,然后直接终止
int s = x + nums[i + 1] + nums[i + 2];
if (s > target) { // 后面无论怎么选,选出的三个数的和不会比 s 还小
if (s - target < minDiff) {
ans = s; // 由于下面直接 break,这里无需更新 minDiff
}
break;
}
优化3: 排除极限情况
如果排序后的数组,当前数,加上后两个数仍然小于target,就代表后续的所有情况都会小于target,我们取最小,然后继续下一轮
s = x + nums[n - 2] + nums[n - 1];
if (s < target) { // x 加上后面任意两个数都不超过 s,所以下面的双指针就不需要跑了
if (target - s < minDiff) {
minDiff = target - s;
ans = s;
}
continue;
}
770

被折叠的 条评论
为什么被折叠?



