题目来源
题目描述
题目解析
思路和leetcode:15. 三数之和一致。
首先考虑枚举第一个元素 a,对于剩下的两个元素 b 和 c,我们希望它们的和最接近 t a r g e t − a target−a target−a。因为此时b + c在数组中范围是没有任何规律的,所以我们只能有一个双重循环来枚举所有的可能情况。因此,我们可以考虑先对数据进行升序排序,这样:
- 假设数组的长度为n,我们先枚举a,它在数组中的位置为i
- 为了防止重复枚举,我们在位置 [ i + 1 , n ) [i +1, n) [i+1,n)的范围内枚举b和c。又因为数组是有序的,所以可以用双指针对这个枚举过程进行优化。我们用 p b p_b pb和 p c p_c pc分别指向b和c的位置。这样,初始时, p b p_b pb指向位置 i + 1 i + 1 i+1,即左边界; p c p_c pc指向 n − 1 n - 1 n−1,即右边界。在枚举的每一个过程中,我们用 a + b + c a + b + c a+b+c来更新答案,并且:
- 如果 a + b + c > = t a r g t e a + b + c >= targte a+b+c>=targte,那么 p c p_c pc左移
- 如果 a + b + c < t a r g t e a + b + c < targte a+b+c<targte,那么 p b p_b pb右移
class Solution {
public:
// 3 <= nums.length <= 1000
int threeSumClosest(vector<int>& nums, int target) {
std::sort(nums.begin(), nums.end());
int ans = 0;
int m = nums.size();
int min = INT_MAX;
auto update = [&](int sum){
int gap = abs(target - sum); // 离target的距离越小越好
if(gap < min){ //本次得到的距离比以前的最小距离还接近 . 那么更新 值
min = gap; //更新最小窗口
ans = sum; //更新ans
}else{ // 本次离target的距离比上一次的大,那么什么也不做
}
};
// 枚举 a
for (int i = 0; i < m - 2; ++i) { // m - 2是为了确保nums[i]后面一定有两个数
// 保证和上一次枚举的元素不相等
if(i > 0 && nums[i] == nums[i - 1]){
continue;
}
int l = i + 1, r = m - 1;
while (l < r){
int sum = nums[l] + nums[r] + nums[i]; // sum 要最接近 target
if(sum == target){
return target;
}
update(sum);
if(sum > target){
--r; // 往小的未搜索的那边走,并且不重复
while (l < r && nums[r] == nums[r + 1]){
--r;
}
}else{
++l;
while (l < r && nums[l] == nums[l - 1]){
++l;
}
}
}
}
return ans;
}
};
类似题目
题目 | 核心思路 |
---|---|
leetcode:15. 三数之和–>无序数组中选出三个数,令其和= 0,返回所有方案(去重) 3Sum | 先排序,然后定下第一个数(枚举),然后左右指针找另外两个数组,如果找到了sum=0的数就去重然后继续找(二分优化) |
leetcode:16. 三数之和—>无序数组中选出三个数,最接近target的sum是什么 3Sum Closest | 先排序,然后固定第一个数(枚举),然后左右指针找另外两个数组,如果找到了sum==target就直接返回,如果找不到就缩小窗口以逼近target(二分优化) |
leetcode:259. 三数之和–>无序数组中选出三个数,其和< target,返回所有的方案个数(不去重) 3Sum Smaller | 先排序,然后固定第一个数(枚举),然后双指针碰撞。将所有符合条件的[l,r]区间都算到结果里面。 |