代码随想录算法训练营第24天 | 回溯算法part01 :● 基础理论 ●77. 组合

基础理论: 

 

  • 也可以叫做回溯搜索法,本质是穷举,不高效
  • 递归不一定会有回溯:比如不断进入更小的问题;但回溯一定是递归带来的
  • 回溯法解决的问题都可以抽象为树形结构。因为回溯法解决的都是在集合中递归查找子集,集合的大小就构成了树的宽度,递归的深度,都构成的树的深度(回溯帮忙做k层for loop)
  • 回溯可以解决的问题包括:

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

代码随想录算法训练营第24天 | 回溯算法part01 :● 基础理论 ●77. 组合_第1张图片

回溯模版:想不出的时候参照模板就不会毫无头绪

void backtracking(参数) {
    if (终止条件) {
        存放结果;
        return;
    }

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

#77 组合

自己没想出来,还没理解组合题怎么用回溯做,所以看了随想录解析

一开始想用多层for loop暴力做发现都做不出,因为可能有k层for loop。回溯的树形结构就是帮忙做k层for loop的,我其实还没想明白层是还是每条枝是for loop。

一个关键:startIdx用来标注比如用3打头的时候,1,2就不要放进去了

原来做二叉树有提到显式和隐式回溯,这里是显,做不了隐

代码随想录算法训练营第24天 | 回溯算法part01 :● 基础理论 ●77. 组合_第2张图片

    vector vec;
    vector> res;

    void backtrack(int n, int k, int startIdx){
        if(vec.size()==k){
            res.push_back(vec);
            return;
        }

        for(int i=startIdx;i<=n;i++){
            vec.push_back(i);
            backtrack(n,k,i+1);
            vec.pop_back();

        }
    }

    vector> combine(int n, int k) {
        backtrack(n,k,1);
        return res;
    }
  • 时间复杂度: O(n * 2^n) (我其实不知道算)
  • 空间复杂度: O(n)

剪枝优化

代码随想录算法训练营第24天 | 回溯算法part01 :● 基础理论 ●77. 组合_第3张图片

原来每个node就代表一个本层的for loop ,总之就是要控制每层的for loop的n,不能到太后面了,所以我们改成:

for (int i = startIndex; i <= n - (k - path.size()) + 1; i++) { // 优化的地方
            path.push_back(i); // 处理节点
            backtracking(n, k, i + 1);
            path.pop_back(); // 回溯,撤销处理的节点
        }

你可能感兴趣的:(代码随想录一刷,算法,数据结构,c++)