40 组合总和 II
给定一个候选人编号的集合 candidates
和一个目标数 target
,找出 candidates
中所有可以使数字和为 target
的组合。
candidates
中的每个数字在每个组合中只能使用 一次 。
** 注意:** 解集不能包含重复的组合。
示例 1:
输入: candidates = [10,1,2,7,6,1,5]
, target = 8
, 输出: [ [1,1,6], [1,2,5], [1,7], [2,6] ]
示例 2:
输入: candidates = [2,5,2,1,2], target = 5, 输出: [ [1,2,2], [5] ]
function combinationSum2(candidates: number[], target: number): number[][] {
candidates.sort((a, b) => a - b);
const n = candidates.length;
const ans = [];
const path = [];
function dfs(i, left) {
// 所选元素之和恰好等于 target
if (left === 0) {
ans.push([...path]);
return;
}
// 没有可以选的数字
if (i === n) {
return;
}
// 所选元素之和无法恰好等于 target
const x = candidates[i];
if (left < x) {
return;
}
// 选 x
path.push(x);
dfs(i + 1, left - x);
path.pop(); // 恢复现场
// 不选 x,那么后面所有等于 x 的数都不选
// 如果不跳过这些数,会导致「选 x 不选 x'」和「不选 x 选 x'」这两种情况都会加到 ans 中,这就重复了
i++;
while (i < n && candidates[i] === x) {
i++;
}
dfs(i, left);
}
dfs(0, target);
return ans;
};
2
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
35
36
37
38
39
40
为了方便跳过相同元素和剪枝,在递归前,把 candidates 从小到大排序。
用 dfs (i,left) 来回溯,设当前枚举到 candidates [i],剩余要选的元素之和为 left,按照选或不选分类讨论:
选 candidates [i]:递归到 dfs (i+1,left−candidates [i])。 不选 candidates [i]:跳过后续所有等于 candidates [i] 的数,递归到 dfs (i ,left),其中 [i,i−1] 中的数都相同。如果不跳过这些数,设 x=candidates [i], x =candidates [i+1],那么「选 x 不选 x 」和「不选 x 选 x 」这两种情况都会加到答案中,这就重复了。 递归边界:
如果 left=0,说明所选元素之和恰好等于 target,把 path 加入答案,返回。 如果 i=n,没有可以选的数字,返回。 如果 left<candidates [i],由于后面的数都比 left 大,所以 left 无法减成 0,返回。 递归入口:dfs (0,target)。