回溯算法整理Day01

 理论基础 

其实在讲解二叉树的时候,就给大家介绍过回溯,这次正式开启回溯算法,大家可以先看视频,对回溯算法有一个整体的了解。

题目链接/文章讲解:代码随想录

视频讲解:带你学透回溯算法(理论篇)| 回溯法精讲!_哔哩哔哩_bilibili

1 回溯的本质时穷举,穷举所有可能找出我们的答案

2  回溯法能解决的问题

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

3 回溯模板

一般用一个path 和result 来收集结果

如果是每一次都要处理一个元素 ,则在for循环外操作即可

1 回溯函数参数和返回值

2 回溯函数的执行逻辑

for循环里对本层进行一个处理,然后backTracking

3 回溯函数的终止条件

组合

对着 在 回溯算法理论基础 给出的 代码模板,来做本题组合问题,大家就会发现 写回溯算法套路。

在回溯算法解决实际问题的过程中,大家会有各种疑问,先看视频介绍,基本可以解决大家的疑惑。

本题关于剪枝操作是大家要理解的重点,因为后面很多回溯算法解决的题目,都是这个剪枝套路。 

题目链接/文章讲解:代码随想录

视频讲解:带你学透回溯算法-组合问题(对应力扣题目:77.组合)| 回溯法精讲!_哔哩哔哩_bilibili

剪枝操作:带你学透回溯算法-组合问题的剪枝操作(对应力扣题目:77.组合)| 回溯法精讲!_哔哩哔哩_bilibili

看到题目的第一想法

     用回溯:利用递归的特性把所有结果集都找出来

        数为n,收集的path长度为k

        参数为n,k,startIndex

        执行逻辑为path.add  递归 path.remove

        终止条件 当path的长度为k时就终止

看到代码随想录之后的想法

         和我的方法一样

自己实现过程中遇到的困难

        递归时startIndex要注意传的是i+1,之前传的是startIndex+1

        要从i+1开始递归后续,防止出现重复结果集

        剪枝操作需要理解一下,已经有path.size 个元素  还需要k-path.size个元素

        于是看n剩下的还有没有这么多个  n-(k-path.size)+1 的位置是否在startIndex后面,若在startIndex前面则不可以

class Solution {
    /*
    组合问题是回溯法解决的经典问题,我们开始的时候给大家列举一个很形象的例子,就是n为100,k为50的话,直接想法就需要50层for循环。

    从而引出了回溯法就是解决这种k层for循环嵌套的问题。

    然后进一步把回溯法的搜索过程抽象为树形结构,可以直观的看出搜索的过程。

    接着用回溯法三部曲,逐步分析了函数参数、终止条件和单层搜索的过程。
    */
    List path;
    List> result;
    public List> combine(int n, int k) {
        //n和k 
        //返回1~n的K个数的组合
        //看下从1开始有几个组合,从2开始有几个组合,从3 开始有几个组合。。。。从N开始有几个组合
        //1 递归的终止条件
        //若当前存入的值的size为K则终止
        //2 递归的单层逻辑
        //for循环当前值,把值放入path中,然后再看后续有多少数组满足这个情况,继续往下递归
        //出来时记得回溯
        //3 递归的参数和返回值
        //参数为,n k startIndex(看之后的path从哪开始收集)
        //回溯的理解:大概就是利用递归的特性,把所有的内容都给找出来,比如这个题目
        //我先在递归上一层确定要处理的元素,然后把剩余要处理的元素,交给之后的来处理
        //要记得画图
        if(n==0){
            return null;
        }
        path = new ArrayList<>();
        result = new ArrayList<>();
        backTracking(n,k,1);
        return result;
    }
    void backTracking(int n,int k,int startIndex){
        if(path.size()==k){
            //应该不能直接把path 放入
            //需要弄一个新的list 把path值存入然后再放入result中
            List resultPath = new ArrayList<>();
            /*for(int i=0;i(path))
            result.add(new ArrayList<>(path));
            return ;
        }
        //当前的执行是从哪个index开始 i是可以等于N的
        /*for(int i=startIndex;i<=n;i++){
            path.add(i);
            //这里是传入i+1 因为i是自增的
            backTracking(n,k,i+1);
            path.remove(path.size()-1);
        }*/
        //如果是做剪枝操作的话 path最终大小为K n
        //要如何剪枝? 看至多 如果for循环起始位置之后的所有元素已经不足了,就不需要执行了,进行剪枝
        //当前存放的元素:path.size
        //还需要多少个元素:k-path.size
        //比如 n=4 k=4 时,第一步当path.size=0时 ,n-(k-path.size) = 0
        //我理解的是n-(k-path.size)+1<=startIndex时 循环不执行了,因为下标已经跑到startIndex之前去了,i<=n-(k-path.size)+1 永不满足
        for(int i=startIndex;i<=n-(k-path.size())+1;i++){
            path.add(i);
            //这里是传入i+1 因为i是自增的,之前传的是startIndex+1 是错误的,要执行的是后续的内容,也就是i+1
            backTracking(n,k,i+1);
            path.remove(path.size()-1);
        }
    }
}
控制台

你可能感兴趣的:(算法)