题目
给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
注意:
1、candidates 中的每个数字在每个组合中只能使用一次;
2、所有数字(包括目标数)都是正整数;
3、解集不能包含重复的组合。
示例:
输入: candidates = [10,1,2,7,6,1,5], target = 8,
所求解集为:
[ [1, 7], [1, 2, 5], [2, 6], [1, 1, 6] ]
注意点
1、排序和去除元素比目标值大的比较(if(arr[i] > target) break;),是为了加快搜索;
2、当target等于0时,lists必须添加拷贝list的值(lists.add(new LinkedList(list))),如果直接使用lists.add(list)会导致返回结果为null,因为lists集合存放的是引用数据类型的地址,回溯时会将lists中的list清空;
3、i > start && arr[i] == arr[i - 1]是为了保证每一层元素不重复(解集不能包含重复的组合)。
4、思路:
- 参考:组合总和(DFS实现)
- 不同点:在于每个元素只能使用一次,有重复元素,且结果不可以重复,所以需要保证除根节点外的每一层不能有重复元素。
实现
class Solution {
//记录解集
List<List<Integer>> lists = new LinkedList<>();
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
if(candidates == null || candidates.length == 0 || target < 0) return lists;
//排序
Arrays.sort(candidates);
//DFS
dfs(candidates, 0, target, new LinkedList<Integer>());
return lists;
}
//FDS
public void dfs(int[] arr, int start, int target, LinkedList<Integer> list){
//目标值等于0,添加对应组合,递归结束
if(target == 0){
lists.add(new LinkedList<Integer>(list));
return;
}
for(int i = start; i < arr.length; i ++){
//数组元素比目标值还大,退出循环
if(arr[i] > target) break;
//保证每一层元素不重复,去除重复组合
if(i > start && arr[i] == arr[i - 1]) continue;
list.addLast(arr[i]);
//开始位置为i + 1,保证元素不重复使用
dfs(arr, i + 1, target - arr[i], list);
//回溯删除list中的元素
list.removeLast();
}
}
}