思路1:建立二叉树 + 回溯(左孩子选,右孩子不选)
- 本质上是一颗二叉树,对于数组中的每个数字而言,可以被选择,也可以不被选择。走左边就是被选择,走右边就是不被选择。
- 如何确保一个数字可以重复被选择,下一层遍历的时候,依然从当前下标
index
开始,而不是从index + 1
开始,backtrace(ans, res, target - candidates[index], candidates, index)
- 剪枝条件是什么?
target == 0
- 如何进行回溯?
res.pop_back()
代码1:
void backtrace(vector<vector<int>>& ans, vector<int>& res, int target, vector<int>& candidates, int index) {
if (index == candidates.size()) {
return;
}
if (target == 0) {
ans.push_back(res);
return;
}
// 不选当前位置
backtrace(ans, res, target, candidates, index + 1);
// 选择当前位置
if (target - candidates[index] >= 0) {
res.push_back(candidates[index]);
backtrace(ans, res, target - candidates[index], candidates, index);
// 回溯
res.pop_back();
}
}
vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
vector<vector<int>> ans;
vector<int> res;
backtrace(ans, res, target, candidates, 0);
return ans;
}
思路2:叶子节点是数组最后一个元素
- 和思路一本质上是一棵树,但是对形状进行优化
- 剪枝条件不变
- 通过遍历数组的方式建树,下一轮循环的开始不是
cur_subscript + 1
,而是cur_subscript
(因为当前数字可以被用很多次)
代码2:
void backtrace(vector<vector<int>>& ans, vector<int>& path_res, int target, vector<int>& candidates, int cur_subscript) {
int all_candidates = candidates.size();
if (cur_subscript == all_candidates || target < 0) {
return;
}
if (target == 0) {
ans.push_back(path_res);
return;
}
// 按照数组来建立树
for (int i = cur_subscript; i < all_candidates; i++) {
path_res.push_back(candidates[i]);
backtrace(ans, path_res, target - candidates[i], candidates, i);
path_res.pop_back();
}
}
vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
vector<vector<int>> ans;
vector<int> path_res;
backtrace(ans, path_res, target, candidates, 0);
return ans;
}