这是我参与8月更文挑战的第3天,活动详情查看:8月更文挑战
前言
关于 LeetCode 数组类型题目的相关解法,可见LeetCode 数组类型题目做前必看,分类别解法总结了题目,可以用来单项提高。觉得有帮助的话,记得多多点赞关注哦,感谢!
题目描述
给定一个数组 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]
]
链接:leetcode-cn.com/problems/co…
题解
上一文LeetCode 39 Combination Sum类似, 可通过改造原始 组合数dfs 的模板来实现该算法.
首先, 本题的目标和 leetcode 39 一样, 都是找出符合和为 target 的 candidates 的子数组. 因此, 递归终止条件也一样, 当 target 的值为 0 时, 即可跳出本次递归.
其次, 本题和 39 题不一样的地方在于每个元素只能使用一次, 所以下一次递归的 index 是从 i + 1 开始的.
除此之外, 还有一个约束是结果中不能出现重复的子数组. 会出现重复子数组的原因在于原始的 candidates 数组中可能会出现重复的元素, 比如示例2中 [2,5,2,1,2] 数组, 如果不改动模板的情况下, 最后的结果会出现两次 [1, 2, 2], 这是不符合条件的, 所以必须要判断一下, 当 ans 数组中已经有 [1, 2, 2] 子数组时, 不能再添加了.
有两种办法可以做到不重复添加, 一个是采用 Set, 因为 Set 保证了元素的不重复, 所以可以利用 Set 的性质来保持每个结果的唯一性, 这里要注意 JS 的 Set 对于 Array 不能保证不重复, 我们需要将每个子数组转换为字符串存在 Set 中, 返回时再解构为数组.
第二个方法是在递归调用时就提前跳出, 保证当前面相同的元素已经组合为一个答案时, 不再遍历自己. 具体做法是首先要对数组进行排序, 这样相同的数组就一定相邻.然后在遍历过程中, 判断当前的 index, 如果 index 大于函数参数 s 且当前 index 元素和 index - 1 元素大小相同时, 就跳过. 这个方法有效的原因在于, 递归调用的过程中, 比如candidates 为 [1, 2, 2, 2, 5], 从位置 0 开始遍历, curr 在递归调用过程中依次为 [1], [1, 2], [1, 2, 2], 当 curr 为 [1, 2, 2]时, 此时的 s 为 2, i 也为 2. curr 被 push 进 ans 中, 之后 curr 将最后一个元素 2 pop 出去, 接着执行下一个循环, i = 3, 此时如果再将 2 添加到 curr 中, 显然就重复了, 所以要跳出这层循环. 因此这样就有效避免了重复.
具体代码如下, 可以看着下面的代码一步步分析上面的说法.
/**
* @param {number[]} candidates
* @param {number} target
* @return {number[][]}
*/
var combinationSum2 = function(candidates, target) {
candidates.sort((a, b) => a - b)
let ans = []
const dfs = (candidates, s, target, curr, ans) => {
if (target === 0) {
ans.push([...curr])
return
}
for (let i = s; i < candidates.length; ++i) {
// 避免重复, 跳出本次循环
if (i > s && candidates[i] === candidates[i-1]) continue
// 剪枝操作, 减少递归
if (candidates[i] > target) break
curr.push(candidates[i])
dfs(candidates, i + 1, target - candidates[i], curr, ans)
curr.pop(candidates[i])
}
}
dfs(candidates, 0, target, [], ans)
return ans
};