「这是我参与11月更文挑战的第27天,活动详情查看:2021最后一次更文挑战」
Hope is a good thing, maybe the best of things. And no good thing ever dies.
前言
回溯算法是对树形或者图形结构执行一次深度优先遍历,实际上类似枚举的搜索尝试过程,在遍历的过程中寻找问题的解。
深度优先遍历有个特点:当发现已不满足求解条件时,就返回,尝试别的路径。此时对象类型变量就需要重置成为和之前一样,称为「状态重置」
题目
给定一个数组 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]
]
分析
本题 candidates 中的每个数字在每个组合中只能使用一次。
本题数组 candidates 的元素是有重复的,而 组合总和 是无重复元素的数组 candidates。
难点在于:集合(数组candidates)有重复元素,但还不能有重复的组合。
解题
const combinationSum2 = (candidates, target) => {
candidates.sort((a,b) => a - b ); // 升序排序
const res = [];
const dfs = (start, temp, sum) => { // start是索引 当前选择范围的第一个
if (sum >= target) { // 不继续选了
if (sum == target) { // 满足条件,加入解集
res.push(temp.slice()); // temp是引用,所指向的内存后续还要操作,所以拷贝一份
}
return; // 结束当前递归
}
for (let i = start; i < candidates.length; i++) { // 枚举出当前的选择
if (i - 1 >= start && candidates[i - 1] == candidates[i]) { // 当前选项和左邻选项一样,跳过
continue;
}
temp.push(candidates[i]); // 作出选择
dfs(i + 1, temp, sum + candidates[i]); // 基于该选择,继续选择,递归
temp.pop(); // 上面的递归结束,撤销选择,回到选择前的状态,切入另一分支
}
};
dfs(0, [], 0);
return res;
};
附:leetcode-cn.com/problems/co…
结语
如果这篇文章帮到了你,欢迎点赞👍和关注⭐️。
文章如有错误之处,希望在评论区指正🙏🙏
欢迎关注我的微信公众号,一起交流技术,微信搜索 🔍 :「 五十年以后 」