回溯
一般用于寻找满足某个条件的组合或者路径。可以已理解为对一棵树进行深度优先遍历,能走则走,不能走则回头选择另一条路走。回溯的关键点是,每一步有几种选择,以及怎么选择。
题目
![](https://i-blog.csdnimg.cn/blog_migrate/dda19742addbc8d20036859c8369cfed.png)
分析
需要求所有无重复的组合,而且每个数可以选择多次。
一般对于求无重复组合问题的解法是对数组元素进行顺序遍历,每次选择数字都会面临两个选择:
选择当前数字,若数字可重复选择,则下一次还可以继续选当前数字;
不选择当前数字,且永远后续从下一个数字开始选择。
因此,每次需要进行两个选择,即进行两次不同的递归调用。
class Solution {
//
public List<List<Integer>> combinationSum(int[] candidates, int target) {
List<List<Integer>> res = new ArrayList();
List<Integer> tmpRes = new ArrayList();
cb(candidates, 0, target, res, tmpRes, 0);
return res;
}
private void cb(int[] candidates, int i, int target, List<List<Integer>> res, List<Integer> tmpRes, int sum) {
if (sum == target) {
res.add(new ArrayList<>(tmpRes));
// 此处的tmp是引用,需要创建一个新的列表保存结果,否则后续tmpRes进行元素增减会影响res中的结果
return;
}
if (sum > target) {
return;
}
if (i > candidates.length - 1) {
return;
}
tmpRes.add(candidates[i]);
// 选择当前数字,并且下一个数字也可以选择当前数字
cb(candidates, i, target, res, tmpRes, sum + candidates[i]);
tmpRes.remove(tmpRes.size() - 1);
// 不选当前数字, 之后也都不会再选择当前数字
cb(candidates, i + 1, target, res, tmpRes, sum);
}
}