递归

递归的定义:子程序(或函数)直接调用自己或通过一系列调用语句间接调用自己,是一种描述问题和解决问题的基本方法。

 

首先,看一个简单的递归例子:

#include <iostream>

void countdown (int n);

 

int main()

{

   countdown (4);

   return 0;

}

void countdown (int n)

{

   using namespace std;

   cout << "Counting down ..." << n << endl;

   if (n > 0)

            countdown (n-1);

   cout << n << ": Kaboom!/n";

}

输出结果为:

入栈、出栈顺序:4321001234

 

递归定义由两部分组成:

第一部分锚:列出产生集合中其他元素的基本元素

第二部分给出有基本元素或已有对象产生新对象的构造规则,

一次次地应用这些规则来产生新对象

在程序设计中递归程序的简单定义是一个能调用自身的程序,但一个递归程序不能总是调用自身,否则它将无法停止;所以递归还必须要有一个终止条件,使得程序能够停止调用自身

 

求阶乘的递归:

int Factorial(int n)

{

   if (n == 0) return 1;

   else return n*Factorial(n-1);

 

}

我们来剖析下面这个递归函数

double power(double x, unsigned int n)

{

   if (n == 0) return 1.0;

   else return x * power(x,n-1);

}

24次方

1次调用: power(2,4)

2次调用:       power(2,3)

3次调用:             power(2,2)

4次调用:                  power(2,1)

5次调用:                        power(2,0)

5次调用:                        1

4次调用:                   x

3次调用:               x*x

2调用:          x*x*x

1次调用: x*x*x*x

 

 

 

void reverse()

{

   char ch;

   cin.get(ch);

   if (ch != '/n')

   {

      reverse();

           cout.put(ch);

   }

}

main调用reverse()函数

输入ABCDEF得到结果为:FEDCBA

首先ABCDEF被依次压入栈中,当不满足递归条件ch != '/n'时,就按照FEDCBA的顺序出栈

调用cout.put(ch);

通过这个函数可以实现以相反的顺序打印输入行

与下面的程序进行比较?

void simpleIterativeReverse()

{

   char stack[80];

   register int top = 0;

   cin.getline(stack,80);

   for (top = strlen(stack) - 1; top >= 0; cout.put(stack[top--]));

}

  上面的reverse函数如果改成下面两种情况?

void reverse()
{
   char ch;
   cin.get(ch);
   if (ch != '/n')  
      reverse2();
 cout.put(ch);
}
void reverse()
{
   static char ch;
   cin.get(ch);
   if (ch != '/n')
   {
      reverse3();
   cout.put(ch);
   }
}

间接递归

Receive(buffer)

Decode(buffer);

Decode(buffer)

   Store(buffer);

Store(buffer)

   Receive(buffer);

嵌套递归

int h(int n,int m)

{

   if (n == 0) return 0;

   else if (n > 0 && m == 0) return h(n-1,1);

   else return h(n-1,h(n,m-1));

}

 

 

不合理递归:

Fibonacci数列:

             Fib(n) = n   n<2

             Fib(n)= Fib(n-2) + Fib(n-1)   其他

说明:声明前两个数为01,数列中其他的数都是起两个前驱的和,依此类推:

01123581321345599…………………

递归算法:

unsigned int Fib(unsigned int n)

{

   if (n < 2) return n;

   else return Fib(n-2) + Fib(n-1);

}

分析Fib(6)

Fib(6) =              Fib(4)                      + Fib(5)

     =     Fib(2)      +     Fib(3)               + Fib(5)

     = Fib(0) + Fib(1)    +     Fib(3)               + Fib(5)

     =0  +   1  +   Fib(1)   +       Fib(2)       + Fib(5)

     =0  +   1  +   Fib(1)   +   Fib(0) + Fib(1)    + Fib(5)

 

要计算Fib(6)需要调用Fib()函数25

可以证明,利用递归定义求Fib(n)的加法次数是Fib(n+1)-1。每个加法都带有两次调用,还要加上最开始的调用,所以计算Fib(n)需要调用2*Fib(n+1)-1Fib()函数

所以上述函数的效率极低;

使用迭代算法可以实现

unsigned int Fib(unsigned int n)

{

   if (n < 2) return n;

   else

   {

      register int i = 2, tmp, current = 1, last = 0;

           for (; i <= n; ++i)

           {

              tmp = current;

                    current +=last;

                    last = tmp;

           }

           return current;

   }

}

 

 

看完前面的例子,下面研究汉诺塔问题

传说在古代印度的贝拿勒斯圣庙里,
安放了一块黄铜板,板上插了三根宝石柱,在其中一根宝石柱上,
自上而下按由小到大的顺序串有64个金盘。
这就是汉诺塔游戏。要求将左边柱子上的64
金盘按照下面的规则移到右边的柱子上。
规则:
1.
有三根杆子A,B,CB杆上有若干碟子
2.
每次移动一块碟子,小的只能叠在大的上面
3.
把所有碟子从A杆全部移到B杆上
 !     !    !
 !     !    !
 !     !    !
 A     B    C

思路:


设盘子只有一个,则本问题可简化为AB
对于大于一个盘子的情况,逻辑上可分为两部分:
n个盘子和除n个以外的n-1个盘子。如果将除n以外的n-1个盘子看成一个整体,则要解决本问题,可按以下步骤:
a
、将A杆上n-1个盘子借助于B先移到c杆; AC (n-1,A,C,B)
b
、将A杆上第n个盘子从A移到B杆; A
B
c
、将C杆上n-1个盘子借助A移到B杆。 CB n-1,C,B,A

void Hanoi (int n, char A, char B,char C)

{

   if (n == 1) Move(A,B);

   else

   {

      Hanoi (n-1,A,C,B);

           Move(A,B);

           Hanoi (n-1,C,B,A);

   }

}

 

 

 

继续看下面一个递归的例子

#include <iostream>

const int Len = 66;

const int Divs = 6;

void subdivide(char ar[], int low, int high, int level);

int main()

{

   char ruler[Len];

   int i;

   for (i = 1; i < Len - 2; i ++)

            ruler[i] = ' ';

   ruler[Len-1] = '/0';

   int max = Len - 2;

   int min = 0;

   ruler[min] = ruler[max] = '|';

   std::cout << ruler << std::endl;

   for (i = 1; i <= Divs; i ++)

   {

      subdivide(ruler, min, max, i);

           std::cout << ruler << std::endl;

           for (int j = 1; j < Len - 2; j ++)

               ruler[j] = ' ';

   }

   return 0;

}

void subdivide(char ar[], int low, int high, int level)

{

   if (level == 0) return;

   int mid = (high + low) / 2;

   ar[mid] = '|';

   subdivide(ar,low, mid, level-1);

   subdivide(ar,mid,high, level-1);

}

思路基本和汉诺塔类似

 

 


 


 


 


 


 


 


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值