理论
回溯法一般是在集合中递归搜索,集合的大小构成了树的宽度,递归的深度构成的树的深度。
回溯的模板:
void backtracking(参数) {
if (终止条件) {
存放结果;
return;
}
for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
处理节点;
backtracking(路径,选择列表); // 递归
回溯,撤销处理结果
}
}
77. 组合
思路
图中可以发现n相当于树的宽度,k相当于树的深度。图中每次搜索到了叶子节点,我们就找到了一个结果。相当于只需要把达到叶子节点的结果收集起来,就可以求得 n个数中k个数的组合集合。
剪枝
对于该回溯算法,是可以进行剪枝优化的。
举个例子,如果n = 4,k = 4的话,那么第一层for循环的时候,从元素2开始的遍历都没有意义了。 在第二层for循环,从元素3开始的遍历都没有意义了。因为剩余元素的数量加起来也到不了4(k = 4)。
所以,可以剪枝的地方就在递归中每一层的for循环所选择的起始位置。
如果for循环选择的起始位置之后的元素个数 已经不足 我们需要的元素个数了,那么就没有必要搜索了。
代码
class Solution:
def combine(self, n: int, k: int) -> List[List[int]]:
res = []
path = []
def helper(startIndex, n, k):
if len(path) == k:
res.append(path.copy())
return
for i in range(startIndex, n + 1):
path.append(i)
helper(i + 1, n, k)
path.pop()
helper(1, n, k)
return res
剪枝优化
class Solution:
def combine(self, n: int, k: int) -> List[List[int]]:
res = []
path = []
def helper(startIndex, n, k):
if len(path) == k:
res.append(path.copy())
return
for i in range(startIndex, n -(k-len(path)) + 2):
path.append(i)
helper(i + 1, n, k)
path.pop()
helper(1, n, k)
return res