使用默记法来简化递归程序的编写

下面是一个算法题目:
有100个台阶,每次可以走1个台阶,也可以走2个台阶,问走完这100个台阶共有多少种走法?
分析:
假设n代表台阶的数目,sum(n)表示走法的数目,则有如下关系:
1。n=1,则表示只有1个台阶,则只有一种走法,则sum(n)=1;
2。n=2,则表示有2个台阶,这个时候,可以选择一次走两个台阶,或者连续走两次,每次走1个台阶,则sum(n)走法为2。
3。n不为1,也不为2,这个时候有两种情况,一种情况是,上次是走的1个台阶。另外一种情况是,上次是一次走的2个台阶。
于是,sum(n) = sum(n-1) + sum(n-2)。
于是,归纳如下:
sum(n) = 1, n = 1;
              = 2, n = 2;
              = sum(n-1) + sum(n-2), n != 1 且 n != 2
于是,算法可以这样写:
int sum(int n)
{
   if (n == 1) {
         return 1;
   }
   else if (n == 2) {
         return 2;
   }
   else if (n > 0) {
         return (sum(n-1) + sum(n-2));
   }
   else {
         return 0;
   }
}

注意:这里只考虑了算法的实现,没有考虑实际的值可能超过int可以表示的范围的问题。
可以看出,这是一个菲波拉齐数列,可以以非递归的方法来给出一个新的算法。
新的算法如下:

int sum(int n)
{
   int     store[2];
   int     index;
   int     ptr;

   index  = 0;

   if     (n <=0 )
        return 0;

   if (n == 1) {
        return 1;
   }
   else if (n == 2) {
        return 2;
   }
   else {

        store[0] = 1;
        store[1] = 2;

        for (ptr = 3; ptr <= n; ptr++) {
           if (index == 0) {
                 store[0] += store[1];
                 index = 1;
           }          
           else {
                store[1] += store[0];
                index = 0;
           }    
    }        

    if (index == 0)
            return store[1];
     else
            return store[0];
   }
}

这里看起来显得有些冗长,但实际上思路是简单而清晰的,也就是利用了两个临时存储单元来轮转存储,从而降低对额外存储空间的需求。
对于这种比较容易得到非递归算法的程序而言,可以以比较简单的方式得到其非递归的算法。
但对于很多的递归算法而言,得到其非递算法就没有那么容易了。下面介绍一般意义上可以利用的办法。
下面我们仔细分析递归算法的一些执行过程,并从中获得对于我们有益的一些东西:

int sum(int n)
{
   if (n == 1) {
        return 1;
   }
   else if (n == 2) {
           return 2;
   }
   else if (n > 0) {
           return (sum(n-1) + sum(n-2));
   }
   else {
           return 0;
   }
}

    通过分析,我们发现递归法的一般思路就是将一个规模比较大的问题转换成比较小规模,容易解决的问题,然后将小规模问题的解逐渐回放,最后得到比较大规模问题的解。
    在这里算法里,计算sum(n),必然要用到对sum(n-1)和sum(n-2)的求解,计算sum(n-1),必然要用到对sum(n-2)和sum(n-3)的求解,但实际上,这里面有些问题的求解是反复进行的,造成了大量的浪费。
    从本算法来看,计算sum(n),必然要用到对sum(n-1)和sum(n-2)的求解。但sum(n-1)和sum(n-2)的求解的规模与sum(n)类似。于是这几乎是指数的增长规模。
    于是,一个新的思路诞生了,为减少运算的规模,可以考虑将计算出的结果保存在数组里面。当下一次需要用到某个计算结果时,如果该结果已经计算出来了,则可以直接使用该计算结果。

下面是新的算法:

long sum(int n)
{
   long total;

   if (cache[n] != -1) {
        return cache[n];
   }

   if (n == 1) {
        cache[n] = 1;
        return 1;
   }
   else if (n == 2) {
        cache[n] = 2;
        return 2;
   }
   else if (n > 0) {
        total = (sum(n-1) + sum(n-2));
        cache[n] = total;
        return total;
   }
   else {
        cache[n] = 0;
        return 0;
   }
}

这个算法需要事先定义一个结果数组,用于存放已经计算出来的sum的结果,可以以static的方式存放。
例如:
static int cache[MAX_SIZE];
定义为static的好处是可以使其他模块中的文件无法看到这个数组。
然后,在调用sum函数之前,必须先初始化这个cache数组,将各个项初始化为-1的值。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值