持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第23天,点击查看活动详情
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]
]
「提示:」
1. 1 <= candidates.length <= 100
2. 1 <= candidates[i] <= 50
3. 1 <= target <= 30
解题思路
以 [2,5,2,1,2], target = 5 为例。
每次都在做选择,选一个数,然后选下一个,看看当前选择受到哪些限制,去做剪枝:
选过的不能再选
不能产生相同的组合
当和为 target,找到一个正确的解,加入解集。当和 > target,爆掉了,不用继续选了,结束当前递归,继续搜别的分支,找齐所有的解。
给定的数组可能有重复的元素,先排序,使得重复的数字相邻,方便去重。
for 枚举出选项时,加入下面判断,从而忽略掉同一层重复的选项,避免产生重复的组合。比如[1,2,2,2,5],选了第一个 2,变成 [1,2],它的下一选项也是 2,跳过它,因为如果选它,就还是 [1,2]。
if (i - 1 >= start && candidates[i - 1] == candidates[i]) {
continue;
}
当前选择的数字不能和下一个选择的数字重复,给子递归传i+1,避免与当前选的i重复。
dfs(i + 1, temp, sum + candidates[i]);
代码实现
const combinationSum = (candidates, target) => {
const res = [];
const dfs = (start, temp, sum) => {
if (sum >= target) { // 爆掉了,不用继续选数了
if (sum == target) { // 加入解集
res.push(temp.slice()); // temp的拷贝
}
return; // 结束当前递归
}
for (let i = start; i < candidates.length; i++) { // 枚举出选择,从start开始
temp.push(candidates[i]); // 加入“部分解”
dfs(i, temp, sum + candidates[i]); // 往下继续选择,同时sum累加上当前数字
temp.pop(); // 撤销选择
}
};
dfs(0, [], 0);
return res;
};
如果你对这道题目还有疑问的话,可以在评论区进行留言;