代码随想录算法训练营第二十四天 | 回溯算法理论基础、77. 组合

119 阅读3分钟

回溯算法理论基础

代码随想录视频讲解

代码随想录文章讲解

什么是回溯法

  • 回溯法也可以叫做回溯搜索法,它是一种搜索的方式
  • 回溯是递归的副产品,只要有递归就会有回溯

回溯法的效率

  • 回溯的本质是穷举,穷举所有可能,然后选出我们想要的答案
  • 如果想让回溯法高效一些,可以加一些剪枝的操作,但也改不了回溯法就是穷举的本质

回溯法解决的问题

  • 组合问题:N个数里面按一定规则找出k个数的集合
  • 切割问题:一个字符串按一定规则有几种切割方式
  • 子集问题:一个N个数的集合里有多少符合条件的子集
  • 排列问题:N个数按一定规则全排列,有几种排列方式
  • 棋盘问题:N皇后,解数独等等
  • 组合是不强调元素顺序的,排列是强调元素顺序

如何理解回溯法

  • 回溯法解决的问题都可以抽象为树形结构
  • 因为回溯法解决的都是在集合中递归查找子集,集合的大小就构成了树的宽度,递归的深度,都构成的树的深度
  • 递归就要有终止条件,所以必然是一棵高度有限的树(N叉树)。

回溯法模板

void backtracking(参数) {
    if (终止条件) {
        存放结果;
        return;
    }
​
    for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
        处理节点;
        backtracking(路径,选择列表); // 递归
        回溯,撤销处理结果
    }
}
  • for循环可以理解是横向遍历,backtracking(递归)就是纵向遍历

77. 组合

代码随想录视频讲解

代码随想录文章讲解

回溯法三部曲

  • 递归函数的返回值以及参数

    在这里要定义两个全局变量,一个用来存放符合条件单一结果,一个用来存放符合条件结果的集合。

  • 回溯函数终止条件

    什么时候到达所谓的叶子节点了呢?

    path这个数组的大小如果达到k,说明我们找到了一个子集大小为k的组合了,在图中path存的就是根节点到叶子节点的路径。

  • 单层搜索的过程

    for循环每次从startIndex开始遍历,然后用path保存取到的节点i。

class Solution:
    def combine(self, n: int, k: int) -> List[List[int]]:
        path = []
        res = []
​
        def _backtracking(n, k, start_index):
            nonlocal path
            nonlocal res
            if len(path) == k:
                res.append(path[:])
                return
            for i in range(start_index, n+1):
                path.append(i)
                _backtracking(n, k, i+1)
                path.pop()
​
        _backtracking(n, k, 1)
​
        return res

剪枝

  • 如果for循环选择的起始位置之后的元素个数 已经不足 我们需要的元素个数了,那么就没有必要搜索了
class Solution:
    def combine(self, n: int, k: int) -> List[List[int]]:
        path = []
        res = []
​
        def _backtracking(n, k, start_index):
            nonlocal path
            nonlocal res
            if len(path) == k:
                res.append(path[:])
                return
            # trim here
            for i in range(start_index, n - (k - len(path)) + 2):
                path.append(i)
                _backtracking(n, k, i+1)
                path.pop()
​
        _backtracking(n, k, 1)
​
        return res