(24/60)回溯理论基础、组合

回校补卡~(回来之后反而懒了

回溯理论基础

什么是回溯法

回溯本质上就是穷举,一种纯暴力搜索。

应用

使用原因

问题很复杂,没有更好的办法,只能暴力搜索。

解决的问题

  1. 组合问题:N个数里面按一定规则找出k个数的集合
  2. 排列问题:N个数按一定规则全排列,有几种排列方式(有序的组合)
  3. 切割问题:一个字符串按一定规则有几种切割方式
  4. 子集问题:一个N个数的集合里有多少符合条件的子集
  5. 棋盘问题:N皇后,解数独等等

理解回溯法

所有回溯法的问题都可以抽象为树形结构。

集合大小构成树的宽度,递归深度构成树的深度。

回溯模板

回溯三部曲:

  1. 返回值及参数。返回值一般是void,回溯算法需要的参数不像二叉树递归的时候那么容易一次性确定下来,所以一般是先写逻辑,然后需要什么参数,就填什么参数。
  2. 终止条件。类似二叉树。
  3. 回溯搜索的遍历过程。回溯法一般是在集合中递归搜索,集合的大小构成了树的宽度,递归的深度构成的树的深度。
void backtracking(参数) {
    if (终止条件) {
        存放结果;
        return;
    }

    for (选择:本层集合中元素(树中节点孩子的数量就是集合的大小)) {
        处理节点;
        backtracking(路径,选择列表); // 递归
        回溯,撤销处理结果
    }
}

for循环可以理解是横向遍历,backtracking(递归)就是纵向遍历,这样就把这棵树全遍历完了,一般来说,搜索叶子节点就是找的其中一个结果了。


组合

leetcode:77. 组合

回溯法

思路

  1. 需要的参数,n,k必须的,begin是开始的节点。
  2. 终止条件:叶子节点。path的元素数量达到k时,就是叶子节点。
  3. 循环部分:把元素放入path中

复杂度分析

时间复杂度:O(C(N,K))。

空间复杂度:result 向量的空间复杂度取决于最终的输出结果,最坏情况下需要存储 C(n, k) 个长度为 k 的组合,因此空间复杂度为 O(C(n, k) * k)。递归调用的空间复杂度取决于递归栈的深度,最坏情况下为 O(k)。综合考虑,总体空间复杂度为 O(C(n, k) * k + k)。

注意点

  1. 递归后记得path.pop_back()。(回溯关键步骤)

代码实现

class Solution {
public:
    vector<vector<int>> result;
    vector<int> path;
    void backtracking(int n,int k,int begin){
        // 叶子节点结束,path的元素达到k就是叶子节点
        if(path.size() == k){
            result.push_back(path);
            return;
        }

        for(int i = begin;i <= n;i++){
            path.push_back(i);
            backtracking(n,k,i + 1);
            path.pop_back();	// 回溯关键
        }
    }

    vector<vector<int>> combine(int n, int k) {
        
        backtracking(n,k,1);
        return result;
    }
};
posted @ 2024-02-23 00:40  Tazdingo  阅读(174)  评论(0)    收藏  举报