持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第44天,点击查看活动详情.
216. 组合总和 III
题目描述
找出所有相加之和为 n 的 k 个数的组合,且满足下列条件:
-
只使用数字1到9
-
每个数字 最多使用一次
返回 所有可能的有效组合的列表 。该列表不能包含相同的组合两次,组合可以以任何顺序返回。
解题思路
思路一: 回溯
对于这类寻找所有可行解的题,我们都可以尝试用搜索回溯
的方法来解决。
【回溯问题解体步骤】
判断当前情况是否非法,如果非法就立即返回;
当前情况是否已经满足递归结束条件,如果是就将当前结果保存起来并返回;
当前情况下,遍历所有可能出现的情况并进行下一步的尝试;
递归完毕后,立即回溯,回溯的方法就是取消前一步进行的尝试
回溯解题框架如下:
res = [];
backtrace(路径, 选择列表) {
if (满足条件) {
res.push(路径)
return
}
for (选择 in 选择列表) {
做选择
backtrace(路径, 选择列表)
撤销选择
}
}
套用模板就行。
实现代码如下:
/**
* @param {number} k
* @param {number} n
* @return {number[][]}
*/
var combinationSum3 = function(k, n) {
let res = [];
backtrace(1, k, [], n, res);
return res;
};
function backtrace(begin, k, path, target, res) {
// 结束条件
if (target == 0 && path.length == k) {
res.push([...path]);
return;
}
// 选择列表
for (let i = begin; i < 10; i++) {
// 大剪枝
if (target - i < 0) return;
// 选择
path.push(i);
// 递归 i + 1, target - i
backtrace(i + 1, k, path, target - i, res);
// 回溯 撤销选择
path.pop();
}
}
思路二: 组合枚举
- tmp 数组存放已经被选出的数字
- 定义
dfs(curr, n, k, sum, res)
函数 , curr从1开始, 在整个递归调用的过程中,curr 是从小到大递增的, 对于curr, 我们可以选择取或者不取
剪枝情况: tmp 长度加上区间 [cur, n] 的长度小于 k 或者tmp数组长度大于k
当tmp数组长度等于k并且和等于n, 即把结果加入结果集中并返回;
实现代码如下:
/**
* @param {number} k
* @param {number} n
* @return {number[][]}
*/
var combinationSum3 = function(k, n) {
let tmp = [],
res = [];
const dfs = (curr, n, k, sum, res) => {
// 剪枝:tmp 长度加上区间 [cur, n] 的长度小于 k,不可能构造出长度为 k 的 tmp
if (tmp.length + (n - curr + 1) < k || tmp.length > k) {
return;
}
if (tmp.length === k && tmp.reduce((prev, next) => prev + next, 0) === sum) {
res.push([...tmp]);
return;
}
tmp.push(curr);
// 考虑选择当前位置
dfs(curr + 1, n, k, sum, res);
tmp.pop();
// 不选择当前位置
dfs(curr + 1, n, k, sum, res);
}
dfs(1, 9, k, n, res);
return res;
};