1、回溯法
由于题目需要返回数字之和为target的不同组合,因此适合使用回溯法进行搜索。相较于常规的深度搜索方法需要将所有的结果都搜索一遍且需要对重复的结果进行去重,我们可以通过在进行深度搜索时引入序号从而减少搜索的次数。
我们在深度搜索时引入序号idx,表示对每一位上的数字进行判断是否需要加入到当前的临时组combine当中。考虑到不添加新值肯定不会超过target,因此我们在每次选择时肯定可以选择使idx + 1即跳过当前的值。若target - candidates[idx] >= 0则说明加入之后当前组合的值仍小于等于target,我们可以将当前的值candidates[idx]加入到combine当中,并继续进行深度搜索,此时目标值从target变为target - candidates[idx]。在深度搜索完成之后将其弹出combine即可。
emplace_back () :在容器尾部添加一个元素,调用构造函数原地构造,不需要触发拷贝构造和移动构造。因此比 push_back () 更加高效。
class Solution {
public:
void dfs(vector<int>& candidates, int target, vector<vector<int>>& ans, vector<int>& combine, int idx) {
if (idx == candidates.size()) {
return;
}
if (target == 0) {
ans.emplace_back(combine);
return;
}
dfs(candidates, target, ans, combine, idx + 1);
if (target - candidates[idx] >= 0) {
combine.emplace_back(candidates[idx]);
dfs(candidates, target - candidates[idx], ans, combine, idx);
combine.pop_back();
}
}
vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
vector<vector<int>> ans;
vector<int> combine;
dfs(candidates, target, ans, combine, 0);
return ans;
}
};
2、回溯+剪枝
考虑到深度优先搜索时实际上会搜索很多无用的结果,因此我们可以添加剪枝操作避免无用搜索。
具体思路同上,我们在进行深度搜索时对每一位考虑是否需要进行添加,但如果添加之后大于target则选择跳出循环,从而避免了无用的搜索。
class Solution {
public:
void dfs(vector<int> &candidates, int begin, int len, int target, vector<int> &path, vector<vector<int>> &res) {
if (target == 0) {
res.push_back(path);
return;
}
for (int i = begin; i < len; i++) {
if (target - candidates[i] < 0) {
break;
}
path.push_back(candidates[i]);
dfs(candidates, i, len, target - candidates[i], path, res);
path.pop_back();
}
}
vector<vector<int>> combinationSum(vector<int> &candidates, int target) {
vector<vector<int>> res;
int len = candidates.size();
if (len == 0) {
return res;
}
sort(candidates.begin(), candidates.end());
vector<int> path;
dfs(candidates, 0, len, target, path, res);
return res;
}
};