利用递归实现动态指定循环层数

10 篇文章 0 订阅
舍友给的一个程序,要求:任意一组确定的数(用户输入),根据一个参数(用户指定),返回一个求和运算的结果。和是这样算的:用户指定参数t的值,从这组数中,任取t个组合数求乘积作为求和的项,取遍所有组合可能,求出总和~~

  这个要咋整呢?我们先来看下t的值:

  t=1,这种情况就是简单的所有数加起来

  t=2,则是从这组数里每两个相乘,把所有组合乘积加起来

  t=3,则是从这组数里每三个相乘,把所有组合乘积加起来

  …………

  悲剧就悲剧在这个省略号上了~~~这个t是用户指定的TT

  既然是把所有组合都计算在内,说专业点就是遍历,那么自然会想到循环咯(利用数据结构和STL也可以解决,有关数据结构的内容以后可能会写到)。对种t=1的情况我们很容易想到一个一重循环,那么t=2呢?自然是二重的,以此类推…………

  唉~~还是看代码吧,先姑且每层都是循环十次吧,这样起码不会太恐惧~~

  for(int i=0;i<10;i++)       ………………第一层

   {

        for(int j=0;j<10;j++)    ………………第二层

       {

            for(int k=0;k<10;k++) ………………第三层

           {

                …………   …………TT里面不知还要多少层 

       }

        }

    }

  由于t的值是程序运行后才能确定的值,而我们的程序到底要写几重循环又得根据t来决定,这样一来,是有点让人头疼,这倒是能联想到OOP模式下的多态,但多态似乎也解决不了问题,这毕竟得依赖代码的书写形式,要几层就得写几层。思来想去,只能把目光放到递归上了,虽然它很让人XX 

  嗯嗯~现在来看下递归代码吧~

        #include<iostream>

        using namespace std;

      void  cycle(int x); //递归函数声明

        int main(){

               int m;

               cout<<"m=";

               cin>>m; //用户输入参数

               cycle(m);

               return 0;

        }

        void cycle(int x){ //递归函数定义

               x--;

               if(x==-1)

                    return;

               for(int i=0;i<10;i++)

                    cycle(x);    //递归调用

        }

  上面就是利用递归实现动态指定循环层数的代码~~貌似比上面的直接循环套循环的代码更简洁 

    我们来简单的看一样这段代码是如何根据参数来嵌套循环的:

  x从一个大于0的数开始递减,在减到-1前,每调用一次cycle(int)都会有一个循环,并且在这个循环的循环体用递归调用了cycle(int)自己,我们都知道函数的运行结束标志是return},当然有时候会因throw而中止,那么在这次递归调用未结束用,这个循环被挂起了,这就相当于前面的那个直接循环嵌套的那段代码中一样:当内循环(第二层、第三层)没有运行完时,第一个循环的控制变量被挂在了i=0上。那么在这个循环体内递归调用自己后会怎样呢?很显然,还有可能会有第二次递归调用(这就由x决定了,而x则是用户传进来的),什么时候终止呢?关键就在x--上了,适当的调整x这个控制变量就可以控制好递归次数,从而控制循环层数。

  当然了,要解决开头提到的那个程序当靠这个代码显然是不行的~~毕竟不是每层循环十次,而且每层的循环次数不一样~~但有了这段代码,离解决问题也就不远了~~~哈!

  接着说刚才那个程序

  要求:任意一组确定的数(用户输入),根据一个参数(用户指定),返回一个求和运算的结果。和是这样算的:用户指定参数t的值,从这组数中,任取t个组合数求乘积作为求和的项,取遍所有组合可能,求出总和~~

  刚才已经解决了利用递归实现循环层数问题,现在来看这个具体问题如何解决。

  这个很容易看出每层循环的次数是不一样的,并且是有联系的。我们先看一下t=3时候的循环嵌套代码:

  for(int i=0;i<10;i++){

       for(int j=i+1;j<10;j++){

          for(int k=j+1;k<10;k++){

               ……

     }

       }

    }

  我们不妨把i,j,k分别看成三个游标指针,不难看出,每层的起始位置均是上层游标所指位置的后一位,这样便可以实现无重复遍历。那么如何用递归现实动态定层呢?先看下刚才那个递归代码:

   #include<iostream>

     using namespace std;

   void  cycle(int x); //递归函数声明

     int main(){

         int m;

         cout<<"m=";

         cin>>m; //用户输入参数

         cycle(m);

         return 0;

     }

     void cycle(int x){ //递归函数定义

        x--;

        if(x==-1)

             return;

        for(int i=0;i<10;i++)

             cycle(x);    //递归调用

     }

  注意红色部分,i=0把循环的起始位置定死了,能不能让它活起来呢?当然可以,如果能很好的利用参数x的话是可以实现的。下面是一段用VC实现的代码:

  #include<iostream>
  #include<stack>    //借用STL提供的栈

  using namespace std;


  void cycle(int x); //递归函数声明
  int op[5]={1,2,3,4,5};  //既然是对一组数求和当然得用数组了,这里因为与数组的交互不是这里要说的重点,所以先把数组写死了,这个组数是可以让用户输入的,还可以使用其它容器类,这里不赘述了
  stack<int> ctr;  //这个栈的作用后面再说
  int a;
  
int b=0;
  int sum=0;    //这里用到了三个全局变量,是为了方便代码的演示,实际写代码时尽量不要用全局变量。这里也可以用局部变量实现

  void main()
  {

     printf("a=");
 
    cin>>a;      //用户指定的参数

 
    int m=0;
     cycle(1); //为什么是1?看实现就明白了

 
    cout<<sum<<endl;
  }


  void cycle(int x) //递归函数的实现
  {
 
     if(b==a)  //递归的结束条件,往后看b是什么

 
    {
  
      
stack<int> temp(ctr);
  
      
int pro=1;
  
      
while(!temp.empty()){
   
        
pro*=op[temp.top()-1];
   
        
temp.pop();
 
       
}
  
      sum+=pro; //这小段是计算部分,先不管

  
      return;
 
    
}
 
    for(int i=x;i<=5;i++) //cycle(int)传进来的参数被拿到这里做循环的起始了,知道前面为什么传1了吧,紧接看下面的递归调用

             
  
      b++;
 
      
 ctr.push(i);
  
      cycle(i+1); //看这里的递归调用,还记得前面说过每层的起始位置均是上层游标所指位置的后一位吧,当在这里递归调用时,这层的控制变量i被挂在了某个值上,由于cycle(int)的参数是里面循环的起始位置,所以传入的i+1便可使递归引起的下一层循环是从i后面一位开始循环的

  
      ctr.pop();
  
      b--;  //那么这个b到底是干什么的呢?还记得前面那个递归么?是靠x递减到-1来控制循环的层数的,而这里x被拿来控制循环的起始位置了,那么层数如何控制呢?就靠这个b了,看下面的分析。

 
    }  

  }

  b:用来记录当前循环层数。它的初始值为0,递归调用前会加1,递归调用后会减1,想到了什么呢?当用户输入的a是一个大于0的数时,就可以进入这个循环体,此时已经进入了第一层循环,b++正确;随后立即递归调用,再做一次b==a的判断,如何用户输入的是一,那么递归就结束了,这时会返回上一级函数运行cycle(i+1)后面的b--,然后循环进入到下一个步值,即i++,之后b又加加,然后再次递归调用。。。。。。

  如何用户输入的是比一大的数呢?一样咯~只不过是会继续递归调用而已,直到b加加到和a相等就到了递归的最深层,这样就实现了循环的控制咯,剩下的就只是如何计算乘积然后求和咯,自然是用到累加器sum了。中间定义了一个累乘器pro,这些都是很清楚的咯。现在就要说明那个栈ctr的作用。

  当进入到最里层的循环的时候,就需要开始作乘积运算了,那些数乘呢?想想前面那三个游标游动的情景,是不是用游标指着的那几个数乘呢?嘿嘿~没错。可以发现没有,所有的游标名字都是局部变量i,局部变量~~~也就是说进入到下一层循环的时候上一层的游标已经被挂起来不可见咯~~所有才用了一个全局的栈来存方它们(也可以用数组,栈更方便嘛,偷懒了~~),当然后也可以用指针之类的东东来代替全局变量,这里图方便啦。ctr.push(i)就是把本层循环的游标压入栈中。ctr.pop(i)又是做什么呢?跟b--一个道理我就不多说啦。

  至于return前面那小段计算部分我想不用再说了吧~~嘿嘿~欧啦

  利用递归实现动态指定循环层数至此告一段落,如果有人有更好的办法,一起交流哈

 

另外一篇介绍不错的:http://simplesource.blog.163.com/blog/static/103414062007616101739493/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值