wuyijia

导航

代码随想录算法训练营第二十四天|回溯算法理论知识,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.接下来看一下优化过程如下:

  1. 已经选择的元素个数:path.size(); ==0

  2. 还需要的元素个数为: k - path.size(); ==4

  3. 在集合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

 

posted on 2023-06-03 12:49  小吴要努力  阅读(18)  评论(0)    收藏  举报