zoukankan      html  css  js  c++  java
  • 递归的思考

    本菜鸡终于开始学算法了,最近遇到递归的问题感觉难以理解,在这里写一些作题的错处与相关的总结



    **递归的总体思维:**
    1.找到停止递归的条件
    2.找到每一层递归之间的联系


    > 问题1**将多层循环简化**
    > 找出从自然数1 、 2、……、m中任取k 个数的所有组合。 例如m=5,k=3
    > 案例:输入5 3 的时候
    > 5 3
    5 4 3
    5 4 2
    5 4 1
    5 3 2
    5 3 1
    5 2 1
    4 3 2
    4 3 1
    4 2 1
    3 2 1


    分析:
    这个问题如果暴力枚举的话,是要用多重循环的,每一列对应一层循环,但是复杂度很大——————>简化:其实每一层的循环都是**相似处理方法**的循环,**每一层之间也有一定的联系**。
    在这个问题中:
    停止递归的条件是:当已经找到了k个数字的时候(这一步可以用计数器来进行记录)
    每一层之间的关系: 第一个数确定的时候,后面的数确定就转化为从k-1个数里找m-1个数的组合----------------->这是递归所体现出来的问题规模缩小

    每一层的输出:*当计数器计数到k的时候,终会输出,我们需要输出的数据:之前前面几层留存的数据,但是在递归过程中难以传递,——————》可以另外设置一个数组,做记忆的作用


    #include <iostream>
    typedef long long ll;
    using namespace std;
    int n,k,ans[100];//把第cur个数字储存起来
    void print()
    {
    
        for(int i=1;i<=k;i++)
            cout<<ans[i]<<' ';
            cout<<endl;
    
    }
    void dfs(int x,int cur)/*ra表示范围是1->x,cur表示他是第几个数*/
    {
        int i;
        if(cur==k+1)   //在cur更新到k的时候循环相应次数并且输出
                print(); 
        for(i=x;i>=1;i--)  /*每次进入循环总是递归到最小的一位开始输出*/
        {
            ans[cur]=i;
            dfs(i-1,cur+1);  //每次进入一个不是末位的数字都要进行一轮循环
        }
    
    }

     此处我自己对这个地方的递归算法一开始想不通的

    1.为什么递归函数入口放在for循环里面----

          在思路里提到过,其实这是一个多重循环,当需要进入递归入口,也就意味着还没到最里层的循环(最里层的函数循环结束之后,就会处处相应的结果啦!!),所以在for里面放递归函数,意味着         每进入一个新的递归函数,就进入了一层新的循环(这也是为什么结束条件是循环到第k数的时候)

    2.当一次递归走到结束之后,发生什么?

      由于递归函数是在for循环里面的,进入递归函数意味着在这一层函数的这一个i,这一次执行的过程中发生递归,(如果条件允许他可能这一层的每个i都会进入递归)

       当一层递归走到结束的时候,会返回到上一层循环,并且继续循环


    2.整数分解

    输出一个正整数n的所有整数和形式。如n=4 

    3
    3=3
    3=2+1
    3=1+2
    3=1+1+1


    分析:这道题是上一道题的一个升级,各层之间的关系与其递归的思想体现在:确定第一个数i之后,剩下的问题就是把n-i分解(问题规模缩小)

              此处要停止递归的条件:没东西可以分了,rest==0;


    #include <iostream>
    typedef long long ll;
    using namespace std;
    int n,rest,bit,store[100];
    
    void print()
    {
        int i=0;
        cout<<n<<'=';
        while(store[i]!=0)
            {
                cout<<store[i];
            if(store[i+1]!=0)
                cout<<'+';
                i++;
            }
        cout<<endl;
    }
    //每一位就是一个拆分的循环
    void dfs(int rest,int bit)   //由于是整数拆分,每次进入递归循环应该都是用rest
    {
        int i;
        for(i=rest;i>=1;i--)
        {
            store[bit]=i;    //储存该位
            if(rest-i==0)    //先写结束条件,单层循环里面(for)rest的值不会更新,所以一点过 
            {store[i+1]=0;     //设置输出断点
    print(); else dfs(rest-i,bit+1);//更新rest和bit的值,不要写成bit++的形式,只是传入值的副本变了 } // return 0; } int main() { ios::sync_with_stdio(0),cin.tie(0); cin>>n; rest=n; dfs(n,0); return 0; }

    出现的问题:

    这个代码写了超级久(T_T)

    问题1:使用了if(rest==0)为结束条件,错误想法:没东西可分,也就是rest==0,我的输出函数使用的是while循环,即使此处不结束,在调用dfs之后rest为0也会结束

    纠正:注意for循环的进入条件:注意递归函数进入条件和输出条件都在for循环里,rest==0,根本无法进入for循环,且多进一层递归函数会导致空间的浪费,记录一位就要检验一次rest还能不能分解

    问题2:在递归函数调用的时候采用了bit++这样的形式传递数据,在一次最内层函数结束之后,跳转到倒数第二层的数组储存位置会出问题,本来应该从0开始,但递归被调了多少次,就离谱多少步

    纠正:函数传递的是数据的副本,使用bit++或者++bit都会使得bit的值产生实际的变化

    问题3:一开始没有设置断点,有些数字储存比较远但并没有被覆盖,在输出的时候会产生错

      解决:1.增加断点

                2.输出的时候获得应输出的长度(bit)用for循环控制输出


    问题升级!!:不能输出重复的,比如4=3+1之后不可以输出4=1+3


    问题分析:在进入新一轮的循环中,我们要比上一轮确定的数字相等或者小


    代码改写:

    void dfs(int pre,int rest,int bit)   //由于是整数拆分,每次进入递归循环应该都是用rest
    {
        int i;
        for(i=pre;i>=1;i--)   //向下传递一个目前函数状态的参数 
        {
            if(rest-i<0)   continue;     //要保证rest-i不小于0,只有i很大的时候才会<0,所以coutine可以使得i不断减小,直到合适的值
            store[bit]=i;
            //if(bit>0&&store[bit]>store[bit-1]) continue;
            if(rest-i==0)    //先写结束条件,单层循环里面(for)rest的值不会更新,所以一点过 
            store[bit+1]=0,
    print(); else dfs(i,rest-i,bit+1);//更新rest和bit的值,不要写成bit++的形式,只是传入值的副本变了 }
  • 相关阅读:
    vue2.0实践的一些细节
    数据库之一
    angularJS实用的开发技巧
    移动端开发的一些技巧总结(2)
    vue入门学习(基础篇)
    JS继承之原型继承
    css3动画
    使用 xlsx 前端解析 excel 文件
    webpack4 打包 library 遇到的坑
    roc-charts 开发笔记:JS广度优先查找无向无权图两点间最短路径
  • 原文地址:https://www.cnblogs.com/wengst/p/12580920.html
Copyright © 2011-2022 走看看