题目链接: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 <= candidates.length <= 100
1 <= candidates[i] <= 50
1 <= target <= 30
文章讲解:代码随想录
视频讲解:回溯算法中的去重,树层去重树枝去重,你弄清楚了没?| LeetCode:40.组合总和II_哔哩哔哩_bilibili
题解1:回溯法
思路:使用回溯法来求解组合问题。
回溯分析:
- 递归函数的参数和返回值:首先创建2个变量 res、path,path 记录遍历的路径,res 记录结果。递归函数的返回值为 void,参数是 start 和 sum,start 记录本次递归的开始位置,sum 记录当前路径和。
- 递归函数的终止条件:找到叶子节点,即路径和等于目标值,也就是找到了一个符合题目要求的组合,将这个组合存入结果数组。当路径和大于目标值时,返回。
- 单层递归的逻辑:使用 for 循环从开始位置 start 开始直到数组末尾进行横向遍历,递归的向下纵向遍历寻找组合。
- 剪枝:先对原数组进行排序,在遍历过程中,如果 sum 加上当前元素后大于 target 了,说明本层后面的结果都会大于 target,就没必要往后遍历了。
- 去重:树层去重,树枝不去重。
/**
* @param {number[]} candidates
* @param {number} target
* @return {number[][]}
*/
var combinationSum2 = function(candidates, target) {
candidates.sort((a, b) => a - b); // 对原数组进行排序
console.log(candidates);
const res = [];
const path = [];
const backtracking = function (start, sum) {
if (sum === target) {
res.push([...path]);
return;
}
if (sum > target) {
return;
}
for (let i = start; i < candidates.length; i++) {
if (i > start && candidates[i] === candidates[i - 1]) {
continue; // 去重,当前数组元素和上一个元素相同时,跳过本次循环
}
if (sum + candidates[i] > target) {
return; // 剪枝
}
path.push(candidates[i]);
backtracking(i + 1, sum + candidates[i]); // 向下查找
path.pop(); // 回溯
}
}
backtracking(0, 0);
return res;
};
分析:时间复杂度为 O(n * 2 ^ n),空间复杂度为 O(n)。
收获
练习使用回溯法求解组合类问题,去重方法和数组中去重一样,要注意树枝去重和树层去重。