Leetcode里有一类这样的题目,给定一个数组和一个target值,寻找数组中的几个数相加等于target值。
1、two sum,寻找数组中两个数相加等于target值
2、three sum,寻找数组中三个数相加等于target值
3、four sum,,寻找数组中四个数相加等于target值
注:three sum和four sum都是two sum问题的延生
4、寻找任意几个数相加等于target值,注:这里面的数组元素都必须都大于0,否则时间复杂度过大(高手可以计算一下)
接下来我们来一一分析各种情况
1、two sum,寻找数组中两个数相加等于target值。
解题思路:最简单直接的是暴利两层循环方法,其时间复杂度为O(n^2),优化解决two sum问题,把时间复杂度降低到O(nlogn),可以使用map,sort等方法,各位可以自己尝试一下,这里主要使用sort来解决问题:
如果把输入数组排序,那么要找到2个数a , b满足 a + b = target。只需要分别指向数组的开头i和结尾j,观察开头和结尾的和,如果两数之和比target小,则说明需要将开头向中间移动一步,如果两数之和比target大,则说明需要将结尾向中间移动一步,如果两数之和和target一样,则找到了满足条件的两个数,将它存起来,然后继续下一步比较(因为可能有不止一个答案),继续下一步比较的办法是把开头i和结尾j同时向中间移动一步。持续这个过程直到开头i和结尾j相遇。
注意:如果数组为{1,1,2,2},target为3,则如果不采取排重措施将会使结果为{{1,2},{1,2}},显然这不是我们想要的结果我们想要的结果仅仅是{{1,2}},在代码中加上排重代码即可进行排重。
<pre name="code" class="cpp">vector<vector<int> > twoSum(vector<int> &num, int target)
{
vector<vector<int> > result;
vector<int> vec;
sort (num.begin(), num.end()); //排序时间复杂度为O(nlogn)
int size = num.size();
int l = 0, r = size - 1;
int sum;
while (l < r)//寻找答案时间复杂度为O(n)
{
//下面的两个if代码是进行排重处理的,也可以尝试其他排重处理,但个人认为在这里进行排重既方便简洁,还减少了一定的时间开销
if (l != 0 && num[l] == num[l-1])
{
++l;
continue;
}
if (r != size - 1 && num[r] == num[r + 1])
{
--r;
continue;
}
sum = num[l] + num[r];
if (sum == target)
{
vec.clear();
vec.push_back(num[l]);
vec.push_back(num[r]);
result.push_back(vec);
}
else if (sum < target)
++l;
else
--r;
}
return result;
}
2、three sum
如果用暴力搜索,3sum问题的时间复杂度为O(N³)借助解决2sum问题的思路,可以优化解决3sum问题。只需要在2sum的搜索过程外层增加一层循环即可,时间复杂度为O(N²),为什么为O(n^2),three sum的sort排序复杂度没有变还是为O(nlogn),其寻找答案时间复杂度变成了O(n^2),所有最终时间复杂度还是为O(n^2)。
vector<vector<int> > threeSum(vector<int> &num) {
vector<vector<int> >result;
vector<int> one;
sort (num.begin(), num.end());
for (int i = 0; i < num.size(); ++i)
{
if (i != 0 && num[i] == num[i-1])//没有这层去重就会存在超时
continue;
int two_sum = 0 - num[i];
int l = i + 1;
int r = (int)num.size() - 1;
while (l < r)
{
if (l != i + 1 && num[l] == num[l-1])
{
++l;
continue;
}
if (r != num.size() - 1 && num[r] == num[r + 1])
{
--r;
continue;
}
int tmp_sum = num[l] + num[r];
if (tmp_sum < two_sum)
++l;
else if (tmp_sum > two_sum)
--r;
else
{
one.clear();
one.push_back(-two_sum);
one.push_back(num[l]);
one.push_back(num[r]);
result.push_back(one);
++l;
--r;
}
}
}
return result;
}
3、four sum
类比解决3sum问题过程,也可以优化解决4sum问题。只需要在2sum过程外层增加2层循环,时间复杂度为O(N³)。
vector<vector<int> > fourSum(vector<int> &num, int target) {
sort(num.begin(), num.end());
vector<int> vec;
vector<vector<int> > result;
int size = num.size(), sumSub, sum;
for (int i = 0; i < size; ++i)
{
if (i != 0 && num[i] == num[i - 1])
continue;
for (int j = i + 1; j < size; ++j)
{
if (j != i + 1 && num[j] == num[j-1])
continue;
int l = j + 1;
int r = size - 1;
sumSub = num[i] + num[j];
while (l < r)
{
if (l != j + 1 && num[l] == num[l - 1])
{
++l;
continue;
}
if (r != size - 1 && num[r] == num[r + 1])
{
--r;
continue;
}
sum = sumSub + num[r] + num[l];
if (sum == target)
{
vec.clear();
vec.push_back(num[i]);
vec.push_back(num[j]);
vec.push_back(num[l]);
vec.push_back(num[r]);
result.push_back(vec);
++l;
--r;
}
else if (sum > target)
--r;
else
++l;
}
}
}
return result;
}
4、寻找任意几个数相加等于target值
解题思路:没有限定选取数字的个数,一般使用DFS方法
class Solution {
private:
vector<vector<int> > ret;
vector<int> a;
public:
vector<vector<int> > combinationSum(vector<int> &candidates, int target) {
sort(candidates.begin(), candidates.end());
getPath(candidates, 0, candidates.size(), target, 0);
return ret;
}
void getPath(vector<int> &cand, int i, int size, int target, int sum)
{
for (int j = i; j < size; ++j)
{
if (j != i && cand[j] == cand[j - 1])
continue;
sum += cand[j];
if (sum == target)
{
a.push_back(cand[j]);
ret.push_back(a);
a.pop_back();
return;
}
else if (sum > target)
{
return;
}
else
{
a.push_back(cand[j]);
getPath(cand, j, size, target, sum);//如果递归的是getPath(cand, j + 1, size, target, sum),则数组中的数只能取1次
a.pop_back();
}
sum -= cand[j];
}
}
};