最接近的三数之和[中等][排序\双指针][三数之和改编]

847 阅读2分钟

题目

image.png

思路

是三数之和的思路的改编 三数之和[中等][双指针\排序] - 掘金 (juejin.cn)

具体步骤如下:

  1. 将数组从小到大进行排序
  2. (外层遍历)从小到大取数字nums[i], 设置target1为target-nums[i], (target1相当于是target - a), 然后从i+1 ~ nums.length-1的范围进行双指针的遍历, 设置begin = i + 1, end = nums.length - 1,开始进入内层遍历,进行判断
  3. (内层遍历-计算偏移量)计算tmp = nums[begin] + nums[end], 将t=target1-tmp, t相当于是target - a - b - c, 即a+b+c和target的偏移量, 将之前的最小偏移量offset和t进行比较, 将绝对值小的赋值给offset
  4. (内层遍历-移动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;
};