题目链接:216. 组合总和 III
题目描述
找出所有相加之和为 n
的 k
个数的组合,且满足下列条件:
- 只使用数字1到9
- 每个数字 最多使用一次
返回 所有可能的有效组合的列表 。该列表不能包含相同的组合两次,组合可以以任何顺序返回。
示例 1:
输入: k = 3, n = 7 输出: [[1,2,4]] 解释: 1 + 2 + 4 = 7 没有其他符合的组合了。
示例 2:
输入: k = 3, n = 9 输出: [[1,2,6], [1,3,5], [2,3,4]] 解释: 1 + 2 + 6 = 9 1 + 3 + 5 = 9 2 + 3 + 4 = 9 没有其他符合的组合了。
示例 3:
输入: k = 4, n = 1 输出: [] 解释: 不存在有效的组合。 在[1,9]范围内使用4个不同的数字,我们可以得到的最小和是1+2+3+4 = 10,因为10 > 1,没有有效的组合。
提示:
2 <= k <= 9
1 <= n <= 60
文章讲解:代码随想录
视频讲解:和组合问题有啥区别?回溯算法如何剪枝?| LeetCode:216.组合总和III_哔哩哔哩_bilibili
题解1:回溯法
思路:使用回溯法来求解组合问题。
回溯分析:
- 递归函数的参数和返回值:首先创建3个变量 res、path 和 sum,path 记录遍历的路径,sum 记录当前路径和,res 记录结果。递归函数的返回值为 void,参数是 n 和 k,还有一个 start 来记录本次递归的开始位置。
- 递归函数的终止条件:找到叶子节点,即 path 的长度和 k 相同,且路径和等于目标值,也就是找到了一个符合题目要求的组合,将这个组合存入结果数组。
- 单层递归的逻辑:使用 for 循环从开始位置 start 开始直到 n 横向遍历,递归的向下纵向遍历寻找组合。
- 剪枝:path 的长度为已选择的元素数,如果剩余的元素数小于 k - path.length,即这条路径找不到符合要求的组合了,就可以做剪枝;当路径和比目标值大时,也应该做剪枝。
/*** @param {number} k* @param {number} n* @return {number[][]}*/
var combinationSum3 = function(k, n) {if (n > k * (9 - (k - 1) + 9) / 2 || n < k * (1 + (1 + (k - 1))) / 2) {return []; // 若无符合条件的结果,直接返回空}const res = []; // 结果数组const path = []; // 路径let sum = 0; // 当前路径和// 回溯函数const backtracking = function (k, n, start) {if (path.length > k) {return; // 路径长度超过 k,直接返回}if (path.length === k) {// 路径长度为 k,符合要求则加入结果数组,然后返回sum === n && res.push([...path]);return;}for (let i = start; i <= 9 - (k - path.length) + 1; i++) { // 剪枝if (sum + i > n) {return; // 剪枝,如果路径和超过目标值,则再往下也找不出结果了,直接返回}// 记录路径path.push(i);sum += i;backtracking(k, n, i + 1); // 继续向下查找// 回溯path.pop();sum -= i;}}backtracking(k, n, 1);return res;
};
分析:时间复杂度为 O(n * 2 ^ n),空间复杂度为 O(n)。
收获
练习使用回溯法解决组合问题,根据题目内容在合适的代码位置做剪枝操作。