第一部分分析3Sum问题,第二部分分析3Sum Closest问题,由于两个问题的思路很像,所以这里放到一起分析。
其中3Sum问题好像还是计算机科学领域暂未解决的问题之一,当然,还没找到更好的解决方案。
Given an array S of n integers, are there elements a, b, c in S such that a + b + c = 0? Find all unique triplets in the array which gives the sum of zero.
Note:
Elements in a triplet (a,b,c) must be in non-descending order. (ie, a ≤ b ≤ c)
The solution set must not contain duplicate triplets.
For example, given array S = {-1 0 1 2 -1 -4},
A solution set is:
(-1, 0, 1)
(-1, -1, 2)
说的意思很简单,就是:给出一个整型数组,从里面找三个数 a ,b ,c
并且使 a+b+c=0
。找出所有符合条件的三元组解集。并且解集中不能出现重复的解。
按这种思想的代码如下:
class Solution {
public:
vector<vector<int> > threeSum(vector<int> &num) {
vector<vector<int> > res;
if(num.size()<3) return res;//参数检验
sort(num.begin(),num.end()); //先排序
int beg , end , sum;
vector<int> tmp(3,0);
for(int i = 0;i<num.size()-2;++i){
// 去除重复数字的再次查找,避免结果集合中出现重复解
if(num[i]>0)break;
if((i>0) && num[i]==num[i-1]) continue;
beg = i + 1;
end = num.size()-1;
while(beg < end){
sum=num[beg] + num[end] + num[i];
if( sum< 0) ++beg;
else if(sum > 0) --end;
else{
tmp[0] = num[i];
tmp[1] = num[beg];
tmp[2] = num[end];
res.push_back(tmp);
// 同样是去除重复数字的再次查找,避免结果集合中出现重复解
while(beg<end && num[beg]==tmp[1]) ++beg;
while(beg<end && num[end]==tmp[2]) --end;
if(beg>=end) break;
}
}
}
return res;
}
};
在维基百科里有关于3Sum的说明,同时我又去查找了一下StackOverflow,发现目前只有这种复杂度为 $O(N^2)$ 的解决方案,还没有更好的解决方法~~~,也可能是我没有找到。维基百科里说的算法思想便是上面的方法,并给出了伪代码:
sort(S);
for i=0 to n-3 do
a = S[i];
start = i+1;
end = n-1;
while (start < end) do
b = S[start];
c = S[end];
if (a+b+c == 0) then
output a, b, c;
// Continue search for all triplet combinations summing to zero.
start = start + 1
end = end - 1
else if (a+b+c > 0) then
end = end - 1;
else
start = start + 1;
end
end
end
同时,根据维基百科上给出的未解决的计算机科学问题中的算法里有这个问题:可以在次二次时间内解决3SUM问题吗?
https://github.com/bbxytl/LeetCodesOJ/blob/master/Algorithms/15%203Sum/images/pic2.png
Given an array S of n integers, find three integers in S such that the sum is closest to a given number, target. Return the sum of the three integers. You may assume that each input would have exactly one solution.
For example, given array S = {-1 2 1 -4}, and target = 1.
The sum that is closest to the target is 2. (-1 + 2 + 1 = 2).
说的意思很简单,就是:给出一个整型数组,从里面找三个数 a ,b ,c
并且使 a+b+c=0
。找出所有符合条件的三元组解集。并且解集中不能出现重复的解。
sum != target
则判断当前两数之间的距离 dt=abs(sum-target)
和目前已求得的最短距离 dis
哪个更小,如果 dt<dis
,则说明出现了一个更接近 target 的解。则记录下此解的和 res 。按这种思想的代码如下:
class Solution {
public:
int threeSumClosest(vector<int> &num, int target) {
int n=num.size();
if(n<3)return 0;
sort(num.begin(),num.end());
int beg,end,sum=0;
int dis=INT_MAX;
int res;
for(int i=0;i<n-2;++i){
beg=i+1;
end=n-1;
while(beg<end){
sum=num[i]+num[beg]+num[end];
if(sum==target)return sum;
else if(sum<target) ++beg;
else --end;
int dt=(abs(sum-target));
if(dis>dt){
dis=dt;
res=sum;
}
}
}
return res;
}
};
测试集进行测试后:
https://github.com/bbxytl/LeetCodesOJ/blob/master/Algorithms/16%203Sum%20Closest/images/pic1.png
newTarget = target - (num[start]+num[end])
,再使用二分查找查找剩下的那个数字。使用的二分查找返回的数字是找到的比较符合条件的数组中数字的下标。下面是经我改进后的代码:
class Solution {
// 二分查找
int findTarget(vector<int> &num, int start, int end, int target) {
if (start==end) return start;
if (end-start==1)
return abs(num[end]-target) > abs(num[start]-target)
? start : end;
int mid = (start+end)/2;
if (num[mid]==target) return mid;
else if(num[mid]>target)
return findTarget(num, start, mid, target);
else return findTarget(num, mid, end, target);
}
public:
int threeSumClosest(vector<int> &num, int target) {
int res=0;
if(num.size()<=3){
for(auto v : num) res+=v;
return res;
}
sort(num.begin(), num.end());
int start = 0;
int end = int(num.size()-1);
int mindiff = INT_MAX;
while (start<end-1) {
int newTarget = target - (num[start] + num[end]);
int p = findTarget(num, start+1, end-1, newTarget);
int curSum = num[start] + num[end] + num[p];
if (curSum == target) {
return target;
}else if(curSum > target) end--;
else start++;
mindiff = abs(mindiff)>abs(target-curSum) ? target-curSum : mindiff;
}
res=target-mindiff;
return res;
}
};
测试集进行测试后:
https://github.com/bbxytl/LeetCodesOJ/blob/master/Algorithms/16%203Sum%20Closest/images/pic2.png