问题描述
* 地址
问题分析
- 该题是 LeetCode 77. Combinations 的进阶,该题数组中含有重复数字,并且每个元素只能使用一次,要求最终找的的可能性不重复。
- 所以该题与 LeetCode 77. Combinations 需要改进的地方就是:
- 每个元素只使用一次,只要对当前元素完成决策后,假设是nums[i]元素,只要从 i+1位置进一步dfs即可
- 去重的操作。 如果不剪枝,在最后用set去重的话,会存在大量冗余递归。应该在决策时去重,实现剪枝。去重的目的是保证当前层不做重复决策,不选重复的数字,所以有以下两种方法:
- 用set保存当前所有做出的决策,若set中已经存在,不再走这条路
- 因为已经对数组进行了排序,所以可以用preNum 记录上一个所做的决策,若等于preNum,说明重复,则不走。
- 在做全排列时同样涉及到了去重,只不过那个如果不用set,更加麻烦
代码实现
class Solution {
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
if (candidates == null || candidates.length == 0) {
return new ArrayList<>();
}
List<List<Integer>> res = new ArrayList<>();
//先对数组进行排序,方便dfs过程中的剪枝
Arrays.sort(candidates);
findcombinationSum(candidates, 0, target, new ArrayList<>(), res);
return res;
}
// 在begin ~ end 中找出所有能组合成remain的路径,元素可重复使用
public void findcombinationSum(int[] candidates, int begin, int remain, ArrayList<Integer> path, List<List<Integer>> res) {
if (begin == candidates.length || remain == 0) {
if (remain == 0) {
res.add(new ArrayList<>(path));
return;
}
return;
}
// HashSet<Integer> set = new HashSet<>();
for (int i = begin; i < candidates.length; ++i) {
//剪枝,因为candidates已经排序,后边数字越来越大,后序都不用dfs了
if (remain - candidates[i] < 0) {
break;
}
/*
//用set去重
if (set.contains(candidates[i])) {
continue;
}
set.add(candidates[i]);
*/
if (i > begin && candidates[i] == candidates[i - 1]) {
continue;
}
path.add(candidates[i]);
//注意,由于元素不可重复使用,所以是从i+1开始
findcombinationSum(candidates, i + 1, remain - candidates[i], path, res);
path.remove(path.size() - 1);
}
return;
}
}