【力扣刷题】16. 最接近的三数之和【暴力迭代+裁枝+优化】

594 阅读1分钟

「这是我参与11月更文挑战的第 22 天,活动详情查看:2021最后一次更文挑战

原题链接

16. 最接近的三数之和 - 力扣(LeetCode) (leetcode-cn.com)

题目描述

给你一个长度为 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

分析

在一个元素数量起步为 3 的数组中,需要我们选出 3 个元素,使得这三个元素之和,与给定的元素 target 之间的差值为最小(相比于其他 3 个元素之和与 target 的差值)

这里,最简单的一种做法,就是暴力匹配,尝试计算每一种组合与 target 的差值,并保留差值最小时,对应的 3 数之和

代码

var threeSumClosest = function(nums, target) {
    nums.sort((a, b) => a - b);
    let near = sum(0, 1, 2),
        diff = Math.abs(target - near);
    for (let i = 0; i < nums.length; i++) {
        for (let j = i + 1; j < nums.length; j++) {
            for (let k = j + 1; k < nums.length; k++) {
                let curr = sum(i,j,k);
                if(Math.abs(curr-target)<diff){
                    diff = Math.abs(curr-target);
                    near = curr;
                }
            }
        }
    }
    return near;
    function sum(i, j, k) {
        return nums[i] + nums[j] + nums[k];
    }
};

image.png

3 层 for 循环的复杂度为 O(n3)O(n^3),耗时确实比较大

优化

在上一种解法中,我们已经对数组进行了排序,在 i, j 对应的情况下,k 当遍历到一个临界状态,即此刻的 3 数之和从小于 target 跳变到大于 target 时,就没有必要 k 后续的数据,因为后续的 3 数之后的差值必定会大于这个临界值

var threeSumClosest = function(nums, target) {
    nums.sort((a, b) => a - b);
    let near = sum(0, 1, 2),
        diff = Math.abs(target - near);
    for (let i = 0; i < nums.length; i++) {
        for (let j = i + 1; j < nums.length; j++) {
            let preVal = nums[i] + nums[j];
            for (let k = j + 1; k < nums.length; k++) {
                let curr = preVal + nums[k];
                if (Math.abs(curr - target) < diff) {
                    diff = Math.abs(curr - target);
                    near = curr;
                }
                if (curr > target) break;
            }
        }
    }
    return near;

    function sum(i, j, k) {
        return nums[i] + nums[j] + nums[k];
    }
};

image.png

虽然还是 O(n3)O(n^3) 的复杂度,但是裁剪掉了无用的循环,性能和空间消耗好上了不少

优化方向推演

在上一种情况中,我们还可以进一步优化,当 k 遍历到临界值后,j+1 后,k 不从 j 的下一位开始遍历,而是使用上一步最后定位的位置,然后在这个位置附近寻找临界值

每次找到临界值都需要判断这个临界状态前后 2 组 3 数之和的差值,再尝试更新最小差值

优化方案 2

同样是暴力迭代,我们是否可以使用二分查找快速定位到临界值呢?


今天的力扣刷题就分享到这里,感谢大家的阅读~