题目
给定一个数组 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
中的每个数字在每个组合中只能用一次 - 本题
candidates
中的数字是有重复的,而组合总和中是无重复的数组candidates
本题关键在于:集合中有重复数字,但是结果中不能有重复的组合(注意,只是两个组合不能相同,同一个组合内有重复的元素没关系)
我们去重去的是同一树层使用过的元素,同一树枝上的都是一个组合里的元素,不用去重,比如[1,1,2]
,在同一树枝上先选了1
,可以接着选1
,不用去重,但是在同一树层上,选了1
已经选过了1
,就不能重复选取了
强调:树层去重的话,需要对数组排序!(保证相同元素相邻)
回溯三部曲:
- 回溯函数参数和返回值:增加一个
bool
型数组,用来记录同一树枝上的元素是否使用过 - 递归终止条件:
sum == target
或者sum > target
- 单层搜索的逻辑:对于去重操作,在
candidates[i] == candidates[i - 1]
的情况下(这里used
数组只是方便理解,写代码的时候不一定要使用这个标记数组)
used[i - 1] == true
,说明同一树枝candidates[i - 1]
使用过 如[1,1,0]
,说明是同一树枝的重复元素,可以有重复used[i - 1] == false
,说明同一树层candidates[i - 1]
使用过 比如[0,1,0]
,说明同一树层有重复元素,需要跳过
所以我们直接跳过同一树层的重复元素即可,先对数组排序,相同的就跳过:
if(i > start && candidates[i] == candidates[i-1]){
continue;
}
java代码如下:
class Solution {
List<List<Integer>> res = new ArrayList<>();
List<Integer> path = new ArrayList<>();
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
Arrays.sort(candidates);
dfs(candidates,target,0);
return res;
}
private void dfs(int[] candidates, int target,int index){
if(target == 0){
res.add(new ArrayList<>(path));
return;
}
for(int i = index; i < candidates.length; i++){
if(candidates[i] <= target){
if(i > index && candidates[i] == candidates[i-1]){
continue;
}
path.add(candidates[i]);
dfs(candidates,target-candidates[i],i+1);
path.remove(path.size()-1);
}
}
}
}