「这是我参与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];
}
};
3 层 for 循环的复杂度为 ,耗时确实比较大
优化
在上一种解法中,我们已经对数组进行了排序,在 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];
}
};
虽然还是 的复杂度,但是裁剪掉了无用的循环,性能和空间消耗好上了不少
优化方向推演
在上一种情况中,我们还可以进一步优化,当 k 遍历到临界值后,j+1 后,k 不从 j 的下一位开始遍历,而是使用上一步最后定位的位置,然后在这个位置附近寻找临界值
每次找到临界值都需要判断这个临界状态前后 2 组 3 数之和的差值,再尝试更新最小差值
优化方案 2
同样是暴力迭代,我们是否可以使用二分查找快速定位到临界值呢?
今天的力扣刷题就分享到这里,感谢大家的阅读~