40. 组合总和 II
给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的每个数字在每个组合中只能使用一次。
说明: 所有数字(包括目标数)都是正整数。 解集不能包含重复的组合。
示例 1: 输入: candidates = [10,1,2,7,6,1,5], target = 8, 所求解集为: [ [1, 7], [1, 2, 5], [2, 6], [1, 1, 6] ]
示例 2: 输入: candidates = [2,5,2,1,2], target = 5, 所求解集为: [ [1,2,2], [5] ]
这道题涉及回溯和去重(因为给定的candidates数组中可以有重复的元素),难点是去重。可以先把题目抽象成树形结构(回溯题抽象成树形结构方便理解):
如上图(图片摘自《代码随想录》)
我们要做的是利用上used数组对同一树层进行去重以达到不同组合数字不同的效果;利用used数组也可以对同一树枝进行去重达到同一组合不存在重复数字的效果。具体判断条件在下面的代码有
由于是递归,可以参考递归四部曲:
递归作用:从cadidates中的下标为startIndex开始取数,直到取到终止条件
递归参数:candidates和target意义同题意,startIndex表示这一层取数的起始下标
终止条件:sum > target或sum==target
单层逻辑:在这一层从startIndex开始取一个数,取完这个数后再进入下一层,下一层从这一层的取数索引加1开始取,直到取到终止条件。这里定义操作前的状态为回溯的初始状态
代码如下:
class Solution {
List<List<Integer>> result = null;//记录所有的组合
List<Integer> path = null;//记录目前的组合
int sum = 0;
boolean[] used = null;//去重用的数组
void combinationSum1(int[] candidates, int target, int startIndex) {
//递归作用:从cadidates中的下标为startIndex开始取数,直到取到终止条件
//递归参数:candidates和target意义同题意,startIndex表示这一层取数的起始下标
//终止条件:sum > target或sum==target
//单层逻辑:在这一层从startIndex开始取一个数,取完这个数后再进入下一层,下一层从这一层的取数索引加1开始取,直到取到终止条件。这里定义操作前的状态为回溯的初始状态
if(sum > target) return;//由于candidates[i]>0,在这一层sum>target后,那么在后面的层sum都将大于target;
if(sum == target){//符合条件
result.add(new ArrayList(path));//因为add添加的是引用,所以得重新new一个对象,如果直接add(path),那么add的将是同一个引用,对应同一个对象
return;
}
for(int i = startIndex; i < candidates.length; i++){
if(i > 0 && candidates[i] == candidates [i - 1] && used[i - 1] == false)//如果是used[i - 1] == false就是对树枝去重,效果相当于每个组合中不能出现相同的元素。其实这道题去掉used数组也可以,i > 0 && candidates[i] == candidates [i - 1]就够了,只不过used数组在对树结构的题目的去重有普适性,值得我们去研究
continue;
sum += candidates[i];
path.add(candidates[i]);
used[i] = true;//表示这个元素被取了
combinationSum1(candidates, target, i + 1);//下一层从i+1开始取,因为candidates 中的每个数字在每个组合中只能使用一次
sum -= candidates[i];
path.remove(path.size() - 1);
used[i] = false;//回溯,将各个状态量初始化
}
}
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
//先排序方便去重,用used数组去重,抽象成树结构后是对同一树层去重,即同一层不能重复取到一个数字
used = new boolean[candidates.length];//使用new boolean[]初始化,默认值为false
result = new ArrayList<>();
path = new ArrayList<>();
Arrays.sort(candidates);//排序方便去重
combinationSum1(candidates, target, 0);
return result;
}
}
回溯算法的话记得操作完这一层后对各个状态量进行初始化