1. 问题描述:
找出所有相加之和为 n 的 k 个数的组合。组合中只允许含有 1 - 9 的正整数,并且每种组合中不存在重复的数字。
说明:
所有数字都是正整数。
解集不能包含重复的组合。
示例 1:
输入: k = 3, n = 7
输出: [[1,2,4]]
示例 2:
输入: k = 3, n = 9
输出: [[1,2,6], [1,3,5], [2,3,4]]
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/combination-sum-iii
2. 思路分析:
① 分析题目可以知道题目的规模大概是确定的,组合的数字是1~9而且是不允许重复的,我们需要尝试出所有的可能性才可以找到所有相加之和为 n 的 k 个数的组合,很明显这个问题是可以使用递归来解决的,递归就是在不知道的情况下去搜索问题的所有方案,在搜索的过程中找到满足题目条件的结果,所以对于这道题目来是可以使用递归来进行解决的。
② 这个是1-9而且是不允许重复的数字组合,有点类似于填写数独上的数字的问题,只是处理的细节不一样而已,对于这道题目来说,也是需要在for循环中进行递归(类似于数独),比如k = 3, n = 9,当我们尝试出第一个结果[1,2,6],发现往下递归的过程中都是不满足n = 9的,最后将会退回到第二层也就是退回到数字2的情况,这个时候我们需要尝试数字3-9的情况,也就是尝试当前这一层的平行状态,所以会得到[1,3,5]这个结果,所以这个是需要在for循环中进行递归求解的,结合数独的问题会更好理解。
③ 由②可知我们需要在for循环中进行递归,所以我们只需要在方法中传入需要的各个参数即可,记录当前使用数字的数目,记录当前数字的累加和,记录中间结果的一个列表,当前正在递归的数字是哪一个,这样下一次递归的时候肯定大于当前这个数字的,也即规定选择数字的顺序这样就可以笔墨安重复组合的问题。
记录中间结果的列表:我们需要往下递归之前进行剪枝,判断递归下去是否有意义,假如有意义那么需要在递归之前将当前的使用的数字记录下来,最后我们需要在出口中判断出当前使用的数字与累加的和是否等于题目中要求的k与n的值,假如相等那么加入到结果集中,并且需要return。
④ 因为是1~9这个数字而且要求是不重复的,所以数据的规模并不是特别大,使用递归是可以求解出来的。
3. 代码如下:
from typing import List
class Solution:
"""
:param count:当前使用了多少个数字
:param cur: 当前使用的数字的累加和
:param num: 当前使用的数字, 往下递归的时候传入当前数字的下一个数字
:param res: 最后需要返回的结果列表
:param rec: 记录使用的数字中间结果
:return:
"""
def dfs(self, k: int, n: int, count: int, cur: int, num: int, res: List[List[int]], rec: List[int]):
if count == k and cur == n:
# 通过切片操作将记录的结果加入到结果列表中, 使用python的切片操作可以实现拷贝整个列表的副本
new = rec[:]
res.append(new)
# 退回到上一层
return
for i in range(num, 10):
# 剪枝: 只有当前使用的数字小于了k并且累加和使小于等于n的才进行递归
if count < k and cur + i <= n:
# 添加当前的数字
rec.append(i)
# i + 1表示递归当前数字的下一个数字这样可以避免重复
self.dfs(k, n, count + 1, cur + i, i + 1, res, rec)
# 回溯: 弹出列表中加入的上一个元素, 尝试for循环中的下一个数字
rec.pop()
def combinationSum3(self, k: int, n: int) -> List[List[int]]:
res, rec = list(), list()
# 使用dfs搜索即可
self.dfs(k, n, 0, 0, 1, res, rec)
return res
y总视频讲解的思路代码:
from typing import List
class Solution:
# start表示开始的数字, n表示还剩余多少总和, res用来记录答案, rec用来记录中间结果
# n与k在递归过程中都是减少的这样当等于0的时候表示刚好凑好了
def dfs(self, start: int, n: int, k: int, res: List[List[int]], rec: List[int]):
if n == 0:
if k == 0:
res.append(rec[:])
elif k != 0:
for i in range(start, 10):
# 只有当n大于等于i才可以继续递归下去, 这样所有数字的累加的和才小于等于n的
# 这样就可以去除重复方案
if n >= i:
rec.append(i)
# 往下递归的时候开始的数字都是加1的这样可以避免重复
self.dfs(i + 1, n - i, k - 1, res, rec)
rec.pop()
def combinationSum3(self, k: int, n: int) -> List[List[int]]:
res = list()
rec = list()
self.dfs(1, n, k, res, rec)
return res