题目
思路
是三数之和的思路的改编 三数之和[中等][双指针\排序] - 掘金 (juejin.cn)
具体步骤如下:
- 将数组从小到大进行排序
- (外层遍历)从小到大取数字nums[i], 设置target1为target-nums[i], (target1相当于是target - a), 然后从i+1 ~ nums.length-1的范围进行双指针的遍历, 设置begin = i + 1, end = nums.length - 1,开始进入内层遍历,进行判断
- (内层遍历-计算偏移量)计算tmp = nums[begin] + nums[end], 将t=target1-tmp, t相当于是target - a - b - c, 即a+b+c和target的偏移量, 将之前的最小偏移量offset和t进行比较, 将绝对值小的赋值给offset
- (内层遍历-移动begin和end)比较tmp和target,(即比较 b+c 和 target - a)。
-
如果tmp小,则需要变大来贴近target - a, 因此begin++ -
如果tmp大,则需要变小来贴近target - a, 因此end-- -
如果二者相等,则证明b+c == target - a, 即a+b+c == target,可以直接退出循环。
-
解释 为什么可以通过排序然后通过begin和end指针来进行计算?
- 首先是通过第一层遍历,将三数之和的问题转化为两数之和接近于固定值的问题:转化为b+c 接近于target - a(第一层遍历的是a)
- 然后在第二层遍历里面,由于a之前的值已经作为 target- a来参与计算的过程,因此如果有最接近的答案,都已经记录下来了,因此不需要去遍历a前面的值来实现接近于target - a, 只需要遍历后面的值即可, 因此begin = a的index + 1, end = nums.length - 1
注意点:
- 在判断offset的最小值的时候,是判断的绝对值大小,而不是值的大小(因为有正数和负数)
- 在碰到offset为0的时候,可以直接退出循环然后输出答案(因为0表示的就是最接近了,不可能比他再接近)
代码
var closeTarget = function(nums, begin, end, target){
let offset = target - nums[begin] - nums[end];
while(begin < end){
let tmp = nums[begin] + nums[end];
let t = target - tmp;
offset = offset*offset < t*t? offset: t;
if(tmp < target){
begin++;
}else if(tmp > target){
end--;
}else{
offset = 0;
break;
}
}
return offset;
}
/**
* @param {number[]} nums
* @param {number} target
* @return {number}
*/
var threeSumClosest = function(nums, target) {
nums.sort((a, b)=> a-b);
let min = closeTarget(nums, 1, nums.length - 1, target - nums[0]);
for(let i = 1; i < nums.length - 2; i++){
let tmp = closeTarget(nums, i + 1, nums.length - 1, target - nums[i]);
min = tmp*tmp > min*min? min: tmp;
if(min == 0){
break;
}
}
return target - min;
};