这个题目还是nsum类型的,不过比上个题更难。上个题的复杂性不是很高,因为不需要考虑到元素的重复性对结果造成干扰,但这道题的话,还是在“集合”里面找出 nsum = target, 每个元素只能使用一次且最后的结果不能重复。那么对于“集合”里面出现的重复的元素的处理就特别棘手。其实代码和上个题的差不多,其实只多了几行,但是这几行代码我却想了一天,甚至睡觉前还想了一段时间,但是下午在做实验的时候突然大脑一闪,突然想到了思路,我今天刚好想到了二叉树的后续非递归遍历的做法,我在这个题也是借鉴相似的做法。不说别的了,说说思路吧:
我们知道上个题的代码是每次递归传递进去的index和本次的index相同,但是这个题index传递进去的时候要加1,其实这样还远远达不到要求,想想看,“集合”中的重复元素还是没有排除掉,比如集合是“1, 1, 2, 5, 6, 7 ,10”而 target=10,按上题的做法结果肯定会有重复项,‘1, 2, 5’‘1, 7’,因为集合中有两个‘1’,那么我们该如何解决呢?我也想到了一种办法,可以解决问题,但是时间复杂度不够,主要代码如下:
if (target == 0)
{
int end = ret.size();
if (end == 0 || tmp != ret[end - 1])
{
ret.push_back(tmp);
}
return;
}
这样的做法的结果应该是对的,但是重复的判断还是做了,这样没有从根本上解决问题。
真正的做法是使用一个prev记录从vector里面删掉的数,若再次出现这个数就说明该数会导致结果的重复出现,所以当遇到这个数的时候我们跳过,这样就解决问题了。代码如下:
class Solution {
public:
vector<vector<int>> combinationSum2(vector<int>& candidates, int target)
{
/*
这个题和上个题差不多吧
*/
vector<vector<int>> ret;
ret.clear();
vector<int> tmp;
tmp.clear();
sort(candidates.begin(), candidates.end());
_findAllSolve(candidates, ret, tmp, target, 0);
return ret;
}
void _findAllSolve(vector<int>& candidates, vector<vector<int>>& ret, vector<int>& tmp, int target, int index)
{
if (target == 0)
{
/*
int end = ret.size();
if (end == 0 || tmp != ret[end - 1])
{
ret.push_back(tmp);
}
*/
ret.push_back(tmp);
return;
}
else
{
int count = 0;
int prev = 0; //保存tmp.back();可以用它来过滤掉重复元素
for (int i = index; i < candidates.size(); ++i)
{
if (candidates[i] > target)
{
return;
}
if (candidates[i] != prev)
{
tmp.push_back(candidates[i]);
}
else
{
continue;
}
_findAllSolve(candidates, ret, tmp, target - candidates[i], i + 1);
//保存此次vector尾部元素,若下次再遇到该元素直接跳过,避免重复,去掉重复的结果全靠prev
prev = tmp.back();
tmp.pop_back();
}
}
}
};
相应的结果如下: