中等题
类似“三数之和”,同样都是三个循环优化成两个循环,思路也非常类似,一道双指针的练习题。此类题为了降低复杂度肯定要先进行排序,因为没有经过排序的数组所包含的信息比较少。除去一层循环,剩下两个指针就很明确了。
经过排序的数组有以下特征:保持两个指针不变(我们这里至少能确定第一个指针不变),第三个指针右移,三数之和必然增加;第三个指针左移,三数之和必然减小。利用这个单调性,就可以把二、三层循环合并。
分类讨论如下:
- a一定,当a+b+c>target: 只能减小b或c,否则就会离target越来越远。
- a一定,当a+b+c<target: 只能增大b或c。
综上,二层循环设置头尾两个指针。当a+b+c>target,减小c;当a+b+c<target,增大b,直到b=c。同时,对数组中出现的相同元素可以排除,因为题目规定只有一组解。
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
class Solution {
public:
int threeSumClosest(vector<int>& nums, int target) {
sort(nums.begin(),nums.end());
int len = nums.size();
int best = 1e7;
for(int first=0; first<len-2; first++){
if(first > 0 && nums[first] == nums[first-1]) continue; //可能出现重复,只选第一个
int third = len-1; //写在这里是因为third和second是同一级的,也就是每个first循环应该找到一个<second,third>对
int second=first+1;
while(third>second){
int sum = nums[first]+nums[second]+nums[third];
if(sum == target) return target; //相同就直接退出了,相当于剪枝
if(abs(sum-target)<abs(best-target)){
//更新
best = sum;
}
if(sum>target){
int third0 = third-1;
while(second<third0 && nums[third] == nums[third0]){
//排除重复
--third0;
}
third=third0;
}
else{
int second0 = second+1;
while(second0<third && nums[second0] == nums[second]){
++second0;
}
second=second0;
}
}
}
return best;
}
};
int main(){
Solution st;
vector<int> vt = {-1,2,1,-4};
cout<<st.threeSumClosest(vt,3)<<endl;
return 0;
}
总结:这是双指针的一道基本题,在确定了双指针的策略后,最重要的应该是找到数学关系。在本题中,target是已知量,先确定a,则a也变为已知量,表达式变成b+c与target-a最接近。接着分析b+c与target-a的关系,再确定其中的一个量b或c,这就需要具体问题具体分析。