给定一个候选人编号的集合 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的每个数字在每个组合中只能使用 一次 。
注意:解集不能包含重复的组合。 
示例 1:
| 12
 3
 4
 5
 6
 7
 8
 
 | 输入: candidates = , target = 8,输出:
 
 
 
 
 
 
 
 | 
示例 2:
| 12
 3
 4
 5
 6
 
 | 输入: candidates = , target = 5,输出:
 
 
 
 
 
 | 
提示:
- 1 <= candidates.length <= 100
- 1 <= candidates[i] <= 50
- 1 <= target <= 30
本题的难点在于:集合(数组candidates)有重复元素,但还不能有重复的组合。

我在图中将used的变化用橘黄色标注上,可以看出在candidates[i] == candidates[i - 1]相同的情况下:
- used[i - 1] == true,说明同一树枝candidates[i - 1]使用过
- used[i - 1] == false,说明同一树层candidates[i - 1]使用过
为什么 used[i - 1] == false 就是同一树层呢,因为同一树层,used[i - 1] == false 才能表示,当前取的 candidates[i] 是从 candidates[i - 1] 回溯而来的。
而 used[i - 1] == true,说明是进入下一层递归,取下一个数
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 
 | class Solution {List<List<Integer>> res = new ArrayList<>();
 List<Integer> path = new ArrayList<>();
 boolean[] used;
 
 public List<List<Integer>> combinationSum2(int[] candidates, int target) {
 used = new boolean[candidates.length];
 Arrays.fill(used, false);
 Arrays.sort(candidates);
 backtracking(candidates, target, 0, 0);
 return res;
 }
 
 public void backtracking(int[] candidates, int target , int index, int sum){
 
 
 
 if(sum == target){
 res.add(new ArrayList<>(path));
 return;
 }
 
 for(int i = index; i < candidates.length; i++){
 if(sum + candidates[i] > target) continue;
 if(i > index && candidates[i] == candidates[i-1] && used[i-1] == false) continue;
 used[i] = true;
 path.add(candidates[i]);
 backtracking(candidates, target, i+1, sum + candidates[i]);
 used[i] = false;
 path.remove(path.size()-1);
 }
 }
 
 }
 
 | 
- 时间复杂度: $O(n * 2^n)$
- 空间复杂度: $O(n)$
使用 i >index 去重,可以不用used数组,因为只要i大于index必定是同一层的其他数
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 
 | class Solution {List<List<Integer>> res = new ArrayList<>();
 List<Integer> path = new ArrayList<>();
 int sum = 0;
 
 public List<List<Integer>> combinationSum2(int[] candidates, int target) {
 Arrays.sort(candidates);
 backtracking(candidates, target, 0);
 return res;
 }
 
 public void backtracking(int[] candidates, int target , int index){
 
 
 
 if(sum == target){
 res.add(new ArrayList<>(path));
 return;
 }
 
 for(int i = index; i<candidates.length; i++){
 if(sum + candidates[i] > target) continue;
 if(i > index && candidates[i] == candidates[i-1]) continue;
 path.add(candidates[i]);
 sum += candidates[i];
 backtracking(candidates, target, i+1);
 sum -= candidates[i];
 path.remove(path.size()-1);
 }
 }
 
 |