题目描述
给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。
candidates 中的 同一个 数字可以 无限制重复被选取。如果至少一个数字的被选数量不同,则两种组合是不同的。
对于给定的输入,保证和为 target 的不同组合数少于150个。
涉及tag
回溯;剪枝;二叉树;dfs
算法思路
和全排列类型是一种题目。
本题采用两次剪枝的方法,示例代码1中begin变量为了去重,如果子节点中有节点用了候选数组中的数字,它之后的节点就不可以再用,体现第一次剪枝,剪去不同顺序但是相同元素组合的部分,示例代码2中先排序,然后将负数的剪去,省去后面dfs遍历的操作。
示例代码1
public class Solution {
public List<List<Integer>> combinationSum(int[] candidates, int target) {
int len = candidates.length;
List<List<Integer>> res = new ArrayList<>();
if (len == 0) {
return res;
}
Deque<Integer> path = new ArrayDeque<>();
dfs(candidates, 0, len, target, path, res);
return res;
}
private void dfs(int[] candidates, int begin, int len, int target, Deque<Integer> path, List<List<Integer>> res) {
// target 为负数和 0 的时候不再产生新的孩子结点
if (target < 0)
return;
if (target == 0) {
res.add(new ArrayList<>(path));
return;
}
//for循环处理的是树的宽度,dfs的递归处理的是树的深度
for (int i = begin; i < len; i++) {
path.addLast(candidates[i]);
dfs(candidates, i, len, target - candidates[i], path, res);
path.removeLast();
}
}
}
示例代码2
class Solution {
public List<List<Integer>> combinationSum(int[] candidates, int target) {
List<List<Integer>> res = new ArrayList<>();
Deque<Integer> path = new ArrayDeque<>();
if (candidates.length == 0)
return res;
Arrays.sort(candidates);
dfs(candidates, path, target, 0, res);
return res;
}
public void dfs(int[] candidates, Deque<Integer> path, int target, int begin, List<List<Integer>> res) {
if (target == 0) {
res.add(new ArrayList<>(path));
return;
}
//组合中的下一个数字从begin开始寻找,避免由于顺序不同导致的重复
for (int i = begin; i < candidates.length; i++) {
//这里是剪枝,break以后不必再进行下面的操作
if (target - candidates[i] < 0) {
break;
}
if (target > 0) {
path.addLast(candidates[i]);
//这里体现数字可以重复无限次被选取,传入i,i成为新的begin
dfs(candidates, path, target - candidates[i], i, res);
path.removeLast();
}
}
}
}