LeetCode 组合总和 Ⅱ
@author:Jingdai
@date:2020.11.30
题目描述(40题)
给定一个数组
candidates
和一个目标数target
,找出candidates
中所有可以使数字和为target
的组合。
candidates
中的每个数字在每个组合中只能使用一次。说明:
- 所有数字(包括目标数)都是正整数。
- 解集不能包含重复的组合。
示例:
输入: candidates = [2,5,2,1,2], target = 5 输出: [ [1,2,2], [5] ]
思路及代码
一开始看到这个题,想到的处理方法是:对于每个数,有两种方式处理,一个是选取,一个是不选取(有点像01背包)。对当前数处理完之后,再对下一个数进行 dfs ,直到找到结果或处理完每一个数。同时,我们可以先对整个 candidates
数组排序,排序后如果当前节点已经大于 target
了,就可以进行剪枝了,不需要再往后 dfs 了。一图胜千言,看图。
然后根据此思路写出如下代码。
public List<List<Integer>> combination = new LinkedList<>();
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
LinkedList<Integer> tempCombination = new LinkedList<>();
Arrays.sort(candidates);
dfs(candidates, target, 0, tempCombination);
return combination;
}
public void dfs(int[] candidates, int target, int index, LinkedList<Integer> tempCombination) {
if (target == 0) {
combination.add(new LinkedList<Integer>(tempCombination));
return;
}
if (index == candidates.length) {
return;
}
if (candidates[index] > target) {
return;
}
// target >= candidate[i]
// choose index
tempCombination.add(candidates[index]);
target -= candidates[index];
dfs(candidates, target, index+1, tempCombination);
target += candidates[index];
tempCombination.removeLast();
// dont choose index
dfs(candidates, target, index+1, tempCombination);
}
看起来好像完美,但是提交的时候傻了,并过不了,因为题目要求不能有重复的解,而我们方法不能去重。那如何进行去重呢?可以选择对结果集进行去重,但是这样就比较麻烦了,所以换一个思路。
我们刚刚是对于每个数的选择和不选择进行dfs,解空间树的每一层就是对于一个数的选择和不选择。这样不好去重,我们可以换个角度,如图,每次选择一个不同的数,这样可以保证每层的数都不一样。而下层可以选择和上一层一样的数(前提还有剩余的)。解空间树的每一层是之前没有选择的且不同节点的选择情况。
注意,下一次的开始下标是当前下标的下一个,因为如果继续选前面的,一定会和之前的重复。比如先选 2 ,再去选 1 的分支一定会和某个先选 1 再选 2 的分支重复。通过这种方式,就很容易的去除了重复的解。剪枝的思路和之前一样,不再赘述。代码如下。
public List<List<Integer>> combination = new LinkedList<>();
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
LinkedList<Integer> tempCombination = new LinkedList<>();
Arrays.sort(candidates);
dfs(candidates, target, 0, tempCombination);
return combination;
}
public void dfs(int[] candidates, int target, int begin, LinkedList<Integer> tempCombination) {
if (target == 0) {
combination.add(new LinkedList<>(tempCombination));
return;
}
for (int i = begin; i < candidates.length; i++) {
if (i > begin && candidates[i] == candidates[i-1]) {
continue;
}
if (candidates[i] > target) {
return;
}
// choose
target -= candidates[i]; // target >= candidates[i]
tempCombination.add(candidates[i]);
dfs(candidates, target, i+1, tempCombination);
// backtrack
target += candidates[i];
tempCombination.removeLast();
}
}