题目描述
给定一个无重复元素的数组 candidates
和一个目标数 target
,找出 candidates
中所有可以使数字和为 target
的组合。
candidates
中的数字可以无限制重复被选取。
说明
- 所有数字(包括
target
)都是正整数。 - 解集不能包含重复的组合。
样例
输入: candidates = [2,3,6,7], target = 7,
所求解集为:
[
[7],
[2,2,3]
]
输入: candidates = [2,3,5], target = 8,
所求解集为:
[
[2,2,2,2],
[2,3,3],
[3,5]
]
算法
(递归枚举)
- 在每一层搜索中,枚举这个数字添加几次。
- 搜索的终止条件是层数超过的数组的长度或者当前数字组合等于目标值。
- 剪枝:可以先将数组从小到大排序,搜索中如果
sum != target
并且sum+candidates[i] > target
,则可以直接终止之后的递归,因为之后的数字都会比candidates[i]
大,不会再产生答案。
C++ 代码
class Solution {
public:
void solve(int i, vector<int>& candidates, int sum,
vector<int> ch, int target, vector<vector<int>>& ans) {
// 注意这里的 ch 不是传递的引用,而是拷贝,因为在每一层需要枚举添加数字。
if (sum == target) { // 找到目标值,添加答案。
ans.push_back(ch);
return;
}
if (i == candidates.size()) // 超出范围回溯。
return;
if (sum + candidates[i] > target) // 剪枝优化。
return;
while (sum <= target) { // 枚举使用当前数字多少次,注意可以使用 0 次。
solve(i + 1, candidates, sum, ch, target, ans);
sum += candidates[i];
ch.push_back(candidates[i]);
}
}
vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
vector<vector<int>> ans;
sort(candidates.begin(), candidates.end());
vector<int> ch; // ch 记录已选择的数字。
solve(0, candidates, 0, ch, target, ans);
return ans;
}
};
更新版本的 C++ 代码
- 为了和 LeetCode 40. Combination Sum II 的代码保持相似性,特意更新了一版 C++ 代码。
class Solution {
public:
void solve(int i, vector<int>& candidates, int sum,
vector<int> &ch, int target, vector<vector<int>>& ans) {
// 注意这里的 ch 是引用。
if (sum == target) { // 找到目标值,添加答案。
ans.push_back(ch);
return;
}
if (i == candidates.size() || sum > target) // 超出范围回溯。
return;
solve(i + 1, candidates, sum, ch, target, ans);
ch.push_back(candidates[i]);
solve(i, candidates, sum + candidates[i], ch, target, ans);
ch.pop_back();
}
vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
vector<vector<int>> ans;
sort(candidates.begin(), candidates.end());
vector<int> ch; // ch 记录已选择的数字。
solve(0, candidates, 0, ch, target, ans);
return ans;
}
};
大佬,完全背包应该也能做吧
完全背包求方案还不如直接暴力求方案快吧,而且会更复杂。
😯
想问下更新版的c++,第二个solve要回溯的原因是因为修改了sum嘛。是不是修改了潜在方案所代表属性的dfs入口都需要回溯
因为修改了ch,所以要回溯
明白了
同求复杂度分析
简单来讲,最坏复杂度当然是 $O(2^n)$。
谢谢助教,这里没有保存结果的O(n) 吗?看其他类似题都是O(n*2^n)
其实 $2^n$ 也不一定准确,因为每个数字可以用不止一次,所以复杂度基本上没办法分析,算上保存答案,可以简单地理解为 $O(n 2^n)$吧。
好的,谢谢助教!
请问这个复杂度怎么分析?