代码随想录算法训练营第二十四天|回溯算法理论知识,77. 组合
1.属于纯暴力算法
2.能解决组合、切割、子集、排列(强调元素的顺序)、棋盘问题(n皇后,解数独)问题。
3.一般都可以抽象成一个n叉树问题。横方向是for(遍历集合元素,主体:处理节点,递归,回溯(撤销处理节点的情况)),纵方向是递归(一般没有返回值,终止条件是收集结果的时候)。
4.回溯算法模板框架:
1 void backtracking(参数) { 2 if (终止条件) { 3 存放结果; 4 return; 5 } 6 7 for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) { 8 处理节点; 9 backtracking(路径,选择列表); // 递归 10 回溯,撤销处理结果 11 } 12 }
77. 组合
【注意】
1.两层for循环是k=2的组合,三层for循环是k=3的组合.........已经无法实现-----回溯算法可以解决此问题(嵌套for循环),回溯算法就是通过递归来控制有多少层for循环,递归的每一层其实是一个for循环。
2.嵌套多少层递归?====集合里面要求这个大小为2的组合。----递归两层。
3.抽象成一个树型结构。
4.定义全局变量:一维数组:path(访问路径),二维:result(返回结果集)
5.终止条件:达到叶子节点,叶子节点的条件:path.size()==2,再通过result收集结果。
6.每一个中间节点(树中节点孩子的数量就是集合的大小)都是一个for循环。---单层搜索的过程。
7.剪枝操作。优化单词搜索的逻辑。i的至多的一个起始位置。
8.需要startIndex来记录下一层递归,搜索的起始位置。for循环每次从startIndex开始遍历,然后用path保存取到的节点i。
【代码】
1.backtracking(递归函数)通过不断调用自己一直往深处遍历,总会遇到叶子节点,遇到了叶子节点就要返回。
2.接下来看一下优化过程如下:
-
已经选择的元素个数:path.size(); ==0
-
还需要的元素个数为: k - path.size(); ==4
-
在集合n中至多要从该起始位置 : n - (k - path.size()) + 1,开始遍历==1
为什么有个+1呢,因为包括起始位置,我们要是一个左闭的集合。
1 class Solution(object): 2 def combine(self, n, k): 3 """ 4 :type n: int 5 :type k: int 6 :rtype: List[List[int]] 7 """ 8 #全局变量 9 result = [] 10 path = [] 11 12 def backtracking(n,k,startidx): 13 if len(path) == k: 14 result.append(path[:]) 15 #没有返回值 16 return 17 # 剪枝, 最后k - len(path)个节点直接构造结果,无需递归 18 last_startidx = n - (k - len(path)) + 1 19 20 #左闭右闭 21 for x in range(startidx, last_startidx + 1): 22 path.append(x) #x是数值 23 backtracking(n,k,x+1)#递归,往下递归 24 path.pop()#回溯 25 26 backtracking(n,k,1) 27 return result
浙公网安备 33010602011771号