本题是组合总和的加强版,要求不能够有重复的集合,但是集合中可以有重复元素,第一想法是按照正常做法进行然后利用哈希表储存,但是这样做会超时,只能另寻他法,这里可以看看代码随想录的思想,利用一个新的boolean数组判断,具体如下:
class Solution {
List<List<Integer>> res = new ArrayList<>();
LinkedList<Integer> path = new LinkedList<>();
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
if (candidates == null || candidates.length == 0) return res;
//这是关键,因为需要排除重复元素,所以首先需要由小到大排列数组
Arrays.sort(candidates);
boolean[] used = new boolean[candidates.length];
backtrack(candidates, target, 0, 0, used);
return res;
}
private void backtrack(int[] candidates, int target, int sum, int index, boolean[] used) {
if (target == sum) {
res.add(new ArrayList<>(path));
return;
}
for (int i = index; i < candidates.length; i ++) {
//剪枝操作,从这往后的循环sum都比target大,直接终止循环,跳回上一层循环。
if (sum + candidates[i] > target) break;
//判断是否出现重复节点,同层的第一个节点已经被访问过,所以直接跳过
if (i > 0 && candidates[i] == candidates[i - 1] && used[i - 1] == false) {
continue;
}
used[i] = true;
sum += candidates[i];
path.add(candidates[i]);
//妈个蛋,又是这,是i不是index!!!
//backtrack(candidates, target, sum, index + 1, used);
backtrack(candidates, target, sum, i + 1, used);
//回溯
sum -= candidates[i];
path.removeLast();
used[i] = false;
}
}
}
更简单的方法,直接利用当前遍历索引来判断:
class Solution {
List<List<Integer>> res = new ArrayList<>();
LinkedList<Integer> path = new LinkedList<>();
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
if (candidates == null || candidates.length == 0) return res;
Arrays.sort(candidates);
backtrack(candidates, target, 0, 0);
return res;
}
private void backtrack(int[] candidates, int target, int sum, int index) {
if (target == sum) {
res.add(new ArrayList<>(path));
return;
}
for (int i = index; i < candidates.length; i ++) {
if (sum + candidates[i] > target) break;
if (i > index && candidates[i] == candidates[i - 1]) continue;
sum += candidates[i];
path.add(candidates[i]);
//妈个蛋,又是这,是i不是index!!!
//backtrack(candidates, target, sum, index + 1, used);
backtrack(candidates, target, sum, i + 1);
sum -= candidates[i];
path.removeLast();
}
}
}