题目描述
给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的数字可以无限制重复被选取。
说明:
- 所有数字(包括 target)都是正整数。
- 解集不能包含重复的组合。
示例
题解
使用回溯,用递归控制for循环嵌套的数量!
注意剪枝。
代码
class Solution {
List<List<Integer>> res= new ArrayList<>();
List<Integer> temp=new ArrayList<>();
public List<List<Integer>> combinationSum(int[] candidates, int target) {
// 先排序
Arrays.sort(candidates);
deep(0, target, candidates, temp, 0);
return res;
}
public void deep(int nowValue, int target, int[] candidates, List<Integer> tmpRes, int index) {
if (nowValue == target) {
//注意!!!一定要新建!!!否则为空!!!!!
//在 Java 中,参数传递是 值传递,对象类型变量在传参的过程中,复制的是变量的地址。这些地址被添加到 res 变量,但实际上指向的是同一块内存地址,因此我们会看到空的列表对象。需要重新新建。
res.add(new ArrayList<Integer>(tmpRes));
return;
}
if (nowValue > target) {
return;
}
if (nowValue < target) {
// i从index开始,是进行了剪枝
for (int i = index; i < candidates.length; i++) {
tmpRes.add(candidates[i]);
deep(nowValue + candidates[i], target, candidates, tmpRes, i);
tmpRes.remove(tmpRes.size() - 1);
}
}
}
}
性能
注意点
在 Java 中,参数传递是值传递,对象类型变量在传参的过程中,复制的是变量的地址。这些地址被添加到 res 变量,但实际上指向的是同一块内存地址,因此我们会看到空的列表对象。需要重新新建。
当不确定for循环执行几次时,可以使用回溯方法。回溯模板如下:
void dfs()//参数用来表示状态
{
if(到达终点状态)
{
...//根据题意添加
return;
}
if(越界或者是不合法状态) //剪枝
return;
for(扩展方式)
{
if(扩展方式所达到状态合法)
{
修改操作; //根据题意来添加
标记;
if(越界或者是不合法状态) //剪枝
return;
dfs();
还原标记,例如记录和还原深度遍历的路径等等;
}
}
}