16. 最接近的三数之和
题目描述
给定一个包括 n 个整数的数组 nums
和 一个目标值 target
。找出 nums
中的三个整数,使得它们的和与 target
最接近。返回这三个数的和。假定每组输入只存在唯一答案。
示例
输入:nums = [-1,2,1,-4], target = 1
输出:2
解释:与 target 最接近的和是 2 (-1 + 2 + 1 = 2) 。
提示:
- 3 ≤ n u m s . l e n g t h ≤ 1 0 3 3 \le nums.length \le 10^3 3≤nums.length≤103
- − 1 0 3 ≤ n u m s [ i ] ≤ 1 0 3 -10^3 \le nums[i] \le 10^3 −103≤nums[i]≤103
- − 1 0 4 ≤ t a r g e t ≤ 1 0 4 -10^4 \le target \le 10^4 −104≤target≤104
题解:
排序+双指针。
此题在 15. 三数之和 的基础上稍微变形了一下,核心思想仍然没变。
最简单粗暴的方法是:三重循环枚举所有可能的三元组,找到离 target
最近的作为答案,这样的时间复杂度为
O
(
n
3
)
O(n^3)
O(n3)。
我们可以考虑枚举第一个元素 nums[i]
,对于剩下的两个元素 nums[j]
和 nums[k]
,我们希望它们的和最接近 target - nums[i]
。而如果在原来的数组上枚举 nums[j]
和 nums[k]
的话,因为它们是无序的,所以需要二重循环枚举,但是如果是有序(升序)的话,那么我们就可以只需要一重循环即可。
具体操作流程:
-
对数组进行升序排序
-
枚举第一个元素
nums[i]
-
j = i + 1, k = n - 1
-
分情况讨论:
-
如果
nums[i] + nums[j] + nums[k] > target
,可以理解为此时三元组的和离target
较远(太大了),而减小nums[k]
的值可能会减小差距,所以--k
-
如果
nums[i] + nums[j] + nums[k] < target
,可以理解为此时三元组的和离target
较远(太小了),而增加nums[j]
的值可能会减小差距,所以++j
-
如果
nums[i] + nums[j] + nums[k]== target
,此时直接返回target
即可 -
记得更新差值
-
一个小优化:nums[i] == nums[i - 1]
时可以跳过该元素,避免重复同样的操作。
时间复杂度: O ( n 2 ) O(n^2) O(n2)
额外空间复杂度: O ( 1 ) O(1) O(1)
代码:
class Solution {
public:
int threeSumClosest(vector<int>& nums, int target) {
int n = nums.size();
sort(nums.begin(), nums.end());
int gap = INT_MAX;
int ret = -1, t, ans;
for ( int i = 0; i < n; ++i ) {
if ( i && nums[i] == nums[i - 1] ) continue;
for ( int j = i + 1, k = n - 1; j < k; ) {
t = nums[i] + nums[j] + nums[k];
if ( t == target ) return t;
ans = abs( t - target );
if ( ans < gap ) {
gap = ans;
ret = t;
}
if ( t > target ) --k;
else ++j;
}
}
return ret;
}
};
/*
时间:4ms,击败:100.00%
内存:9.5MB,击败:99.96%
*/