理论基础
是什么? 回溯函数也就是递归函数,指的都是一个函数。
本质上模拟的是嵌套for循环,递归是嵌套for循环,递归深度,代表嵌套负循环的次数
能解决什么问题?
组合问题【无序】:N个数里面按一定规则找出k个数的集合
切割问题:一个字符串按一定规则有几种切割方式
子集问题:一个N个数的集合里有多少符合条件的子集
排列问题【有序】:N个数按一定规则全排列,有几种排列方式
棋盘问题:N皇后,解数独等等
如何理解回溯法?—》抽象成“树形结构”
回溯法解决的都是在集合中递归查找子集,集合的大小就构成了树的宽度,递归的深度就构成了树的深度。递归就要有终止条件,所以必然是一棵高度有限的树(N叉树)。
回溯模版
void backtracking(参数){
if (终止条件):
{收集结果;
return ;
}
for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) : #处理集合的每一个元素
{处理节点;
backtracking(路径,选择列表); #递归函数
回溯操作; #撤销处理节点的
}
}
77. 组合
如果是暴力解法,k是递归深度,同时也是for循环嵌套数
for (int i = 1; i <= n; i++) {
for (int j = i + 1; j <= n; j++) {
cout << i << " " << j << endl;
}
}
回溯三步曲
1.递归函数的参数和返回值 : 返回值void ,参数【n, k, startindex,路径path, 总结果result】startIndex 就是防止出现重复的组合
2.终止条件【到叶子结点,得到路径的长度是k,即为len(path) = k,收集结果result.append(path); return 】
3.单层递归的逻辑
未剪枝优化
class Solution:
def combine(self, n: int, k: int) -> List[List[int]]:
result = [] # 存放结果集
self.backtracking(n, k, 1, [], result)
return result
def backtracking(self, n, k, startIndex, path, result):
if len(path) == k:
result.append(path[:])
return
for i in range(startIndex, n + 1): # 需要优化的地方
path.append(i) # 处理节点
self.backtracking(n, k, i + 1, path, result)
path.pop() # 回溯,撤销处理的节点
剪枝优化
class Solution:
def combine(self, n: int, k: int) -> List[List[int]]:
result = [] # 存放结果集
self.backtracking(n, k, 1, [], result)
return result
def backtracking(self, n, k, startIndex, path, result):
if len(path) == k:
result.append(path[:])
return
for i in range(startIndex, n - (k - len(path)) + 2): # 优化的地方,因为index从1开始
path.append(i) # 处理节点
self.backtracking(n, k, i + 1, path, result)
path.pop() # 回溯,撤销处理的节点