递归的思考

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



**递归的总体思维:**
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++的形式,只是传入值的副本变了 }

 

posted @ 2020-03-27 13:47  AUwegst  阅读(239)  评论(0编辑  收藏  举报