经典回溯,但是本题是可以重复利用数字求和的,代码如下:
class Solution {
List<List<Integer>> res = new ArrayList<>();
LinkedList<Integer> path = new LinkedList<>();
public List<List<Integer>> combinationSum(int[] candidates, int target) {
if (candidates == null || candidates.length == 0) return res;
backtrack(candidates, target, 0, 0);
return res;
}
//刚开始方法里我想的是不带index参数,因为本题对原数组的遍历是可重复的
//但是后来发现得到的结果确实正确,但是很多是顺序不同,也就是排列,得到的是排列结果
private void backtrack(int[] candidates, int target, int sum, int index) {
if (target == sum) {
res.add(new ArrayList<>(path));
return;
}else if (target < sum) {
return;
}
//这里原来是i = 0,那么不管递归到那一步,一直都从头开始遍历
for (int i = index; i < candidates.length; i ++) {
sum += candidates[i];
path.add(candidates[i]);
backtrack(candidates, target, sum, i);
path.removeLast();
sum -= candidates[i];
}
}
}
剪枝优化后的回溯:
class Solution {
List<List<Integer>> res = new ArrayList<>();
LinkedList<Integer> path = new LinkedList<>();
public List<List<Integer>> combinationSum(int[] candidates, int target) {
if (candidates == null || candidates.length == 0) return res;
//需要先对candidates由小到大排列,这样后面才可以优化,按照顺序提前判断结果是否大于target
//错误的测试用例放在下面了
Arrays.sort(candidates);
backtrack(candidates, target, 0, 0);
return res;
}
private void backtrack(int[] candidates, int target, int sum, int index) {
if (target == sum) {
res.add(new ArrayList<>(path));
return;
}
for (int i = index; i < candidates.length; i ++) {
//如果当前sum和当前数字的和>target,那么提前就判断出这个循环之后所有值都不符合
//因为candidates按照顺序有小到大排列,此时不符合,那么后面的数更大,更不符合,直接终结循环即可
if (sum + candidates[i] > target) break;
sum += candidates[i];
path.add(candidates[i]);
backtrack(candidates, target, sum, i);
path.removeLast();
sum -= candidates[i];
}
}
}
不加排序的错误测试用例: