链接: 题目链接
题目:
给定一个候选人编号的集合 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的每个数字在每个组合中只能使用 一次 。
注意:解集不能包含重复的组合。
不能包含重复的组合(意思可以通过下面的例子了解一下):
输入: candidates = [10,1,2,7,6,1,5], target = 8,
输出:
[
[1,1,6],
[1,2,5],
[1,7],
[2,6]
]
相信聪明的你发现了,其实就元素在一个组合之间是可以重复的(前提是candidates里面有多个值相同的元素),而组合之间不能相同.
那么如何去重呢?
其实去重也就是使用过的元素不能再选取.
这里就要狠狠地夸一下carl哥的同一树层上不能选取和同一树枝上不能选取.
要了解同一树层或同一树枝的概念,还要从把回溯当做N叉树来看待.
图片: 如上所示:回溯的for循环里面中代表的是同一树层,而递归代表同一枝干.现在大家概念应该很清楚了.
而我们这一题所对应的就是同一树层上相同值不能重复选取.
其实参考上面的[1,1,6]和[1,7]大家就可以发现了,毕竟没有出现两个[1,7]嘛
那么(一些细节代码说明了)代码1如下:
class Solution {
List<List<Integer>> res;
List<Integer> path;
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
//题目速读:
//组合问题,res中的组合不能重复,同时每个数字再每个组合中只能使用一次
//对应出招:
//每个数字每个组合中只能使用一次:设置startIndex来限制for里面对candidates遍历的范围
//不能有重复的组合:弄一个boolean数组or直接借用startIndex(这里直接借用的是startIndex)
res=new ArrayList<>();
path=new ArrayList<>();
//为了剪枝,将candidates排序
//也同时让我们能够更好地去重
Arrays.sort(candidates);
backTracking(candidates,target,0);
return res;
}
private void backTracking(int[] candidates, int target,int startIndex){
if(target==0){
res.add(new ArrayList<>(path));
return;
}Integer w;
for (int i = startIndex; i < candidates.length; i++) {
//过滤掉N叉树同层的值相同的元素
if(i>0&&candidates[i-1]==candidates[i]&&i!=startIndex){
//和我们使用一个Boolean数组一样的效果,核心就在于
//我们排了序,相同的元素肯定是相邻的,所以i!=startIndex就可以完美的处理掉同一层的相同值
//而如果是同一个枝干的则会是i==startIndex
continue;
}
w=candidates[i];
if(target-candidates[i]<0)return;
else{
path.add(w);
backTracking(candidates,target-candidates[i],i+1);
path.remove(w);
}
}
}
}
代码2如下:
class Solution {
List<List<Integer>> res;
List<Integer> path;
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
//不能包含重复的组合,每个数字只能使用一次
//参数:candidates,target,startIndex,返回值:void
//退出条件:当target==0,target小于0用来剪枝
//确定单层递归的逻辑
res=new ArrayList<>();
path=new ArrayList<>();
Arrays.sort(candidates);
boolean[]used=new boolean[candidates.length];
Arrays.fill(used,false);
backTracking(candidates,target,0,used);
return res;
}
private void backTracking(int[] candidates,int target,int startIndex,boolean[]used){
if(target==0){
res.add(new ArrayList<>(path));
}
for (int i = startIndex; i <candidates.length ; i++) {
if(i>0&&candidates[i]==candidates[i-1]&&used[i-1]==false)continue;
Integer w=candidates[i];
if(target-w<0){
return;
}
path.add(w);
used[i]=true;
backTracking(candidates,target-w,i+1,used);
path.remove(w);
used[i]=false;
}
}
}
绝大多数参考自代码随想录