目录
LeetCode216. 组合总和III
链接: 216. 组合总和 III - 力扣(LeetCode)
1. 思路
本题就是在[1,2,3,4,5,6,7,8,9]这个集合中找到和为n的k个数的组合。
暴力思路
最开始想到的就是嵌套K个for循环,比如说K=3 ,就嵌套三个for循环找,但是这里我们并没有确定K到底是多少,K也是一个变量,这时候我们想到回溯算法,用递归来控制嵌套几层for循环。
回溯算法思路
相对于 77. 组合 无非就是多了一个限制,本题是要找到和为n的k个数的组合,而整个集合已经是固定的了[1,...,9]。想到这一点了,做过 77.组合 之后,本题是简单一些了。
本题k相当于了树的深度,9(因为整个集合就是9个数)就是树的宽度。例如 k = 2,n = 4的话,就是在集合[1,2,3,4,5,6,7,8,9]中求 k(个数) = 2, n(和) = 4的组合。选取过程如图:图中,可以看出,只有最后取到集合(1,3)和为4 符合条件;
注意:取2 的时候,剩下的在[ 3…9 ] 中取,为什么不包括1? 因为组合问题不强调元素的顺序,21和12是同一个组合,为了避免重复,不包括1;那为什么也不包括2? 因为组合中每个元素只可以用一次;
2. 代码实现
回溯三部曲
2.1 确定递归函数参数
依然需要一维数组path来存放符合条件的结果,二维数组result来存放结果集;这里我依然定义path 和 result为全局变量。至于为什么取名为path?从上面树形结构中,可以看出,结果其实就是一条根节点到叶子节点的路径。
接下来还需要如下参数:
- targetSum(int)目标和,也就是题目中的n。
- k(int)就是题目中要求k个数的集合。
- sum(int)为已经收集的元素的总和,也就是path里元素的总和。
- startIndex(int)为下一层for循环搜索的起始位置。
class Solution:
def __init__(self):
self.res = []
self.sum_now = 0
self.path = []
def combinationSum3(self, k: int, n: int) -> [[int]]:
self.backtracking(k, n, 1)
return self.res
def backtracking(self, k: int, n: int, start_num: int):
优化:
-
其实这里sum这个参数也可以省略
每次targetSum减去选取的元素数值,然后判断如果targetSum为0了,说明收集到符合条件的结果了,我这里为了直观便于理解,还是加一个sum参数;
-
还要强调一下,回溯法中递归函数参数很难一次性确定下来,一般先写逻辑,需要啥参数了,填什么参数;
2.2 确定终止条件
什么时候终止呢?
在上面已经说了,k其实就已经限制树的深度,因为就取k个元素,树再往下深了没有意义。所以如果path.size() 和 k相等了,就终止。
什么时候收集结果集呢?
如果此时path里收集到的元素和(sum) 和targetSum(就是题目描述的n)相同了,就用result收集当前的结果。
if len(self.path) == k: # len(path)==k时不管sum是否等于n都会返回
if self.sum_now == n:
self.res.append(self.path[:])
return
2.3 单层搜索过程
本题和77.组合区别之一就是集合固定的就是9个数,[1…9],所以for循环固定i≤9;
处理过程就是 path收集每次选取的元素,相当于树型结构里的边,sum来统计path里元素的总和;
for i in range(start_num, 10):
self.path.append(i)
self.sum_now += i
self.backtracking(k, n, **i + 1**)
self.path.pop()
self.sum_now -= i
别忘了处理过程 和 回溯过程是一一对应的,处理有加,回溯就要有减!
为什么下一层递归要取i+1,之前有解释过!!
为什么不取1,为什么不取i? 再粘贴过来一次~
注意:取2 的时候&#