递归(一)

1 一个直接调用自己或通过一系列的调用语句间接地调用自己的函数,称作递归函数

2 在程序设计语言中,实现递归要用到栈

3 递归是程序设计中一个强有力的工具。其一,有很多数学函数是递归定义的,如阶乘函数:

                 1                  若n=0

Fact(n)={

                n*Fact(n-1).   若n>0

其二,有的数据结构,如二叉树 广义表等,由于结构本身固有的递归特性,则他们的操作可递归地描述;其三,还有一类问题,虽然问题本身没有明显的递归结构,但用递归求解比迭代求解更简单,如八皇后问题,Hanoi塔问题等。

例:Hanoi塔问题


(n阶Hanoi塔问题)假设有3个分别命名为X Y Z的塔座,在塔座X上查有n个直径大小各不相同,依小到大编号为1,2,...,n的圆盘,现要求将X轴上的n个圆盘移至塔座Z上并仍按同样顺序折叠,圆盘移动时必须遵循以下规则:

* 每次只能移动一个圆盘

* 圆盘可以插在X Y和Z中的任一塔座上

* 任何时刻都不能将一个较大的圆盘压在较小的圆盘之上

如何实现移动圆盘的操作呢?当n=1时,问题比较简单,只要将编号为1的圆盘从塔座X直接移至塔座Z上即可;当n>1时,需要利用塔座Y作辅助塔座,若能设法将压在编号为n的圆盘之上的n-1个圆盘从塔座X(依照上述法则)移至塔座Y上,则可先将编号为n的圆盘从塔座X移至塔座Z上,然后再将塔座Y上的n-1个圆盘(依照上述法则)移至塔座Z上。而如何将n-1个圆盘从一个塔座移至另一个塔座的问题是一个和原问题具有相同特征属性的问题,只是问题的规模小1,因此可以利用同样的方法求解

代码:(完整代码:git@github.com:hglspace/hanoi.git)

void hanoi(int n,struct sqstack * x,struct sqstack * y,struct sqstack * z){

    void moveAction(struct sqstack * x,struct sqstack * y);

    if(n==1){

        moveAction(x, z);//将编号为1的圆盘从x移到z

    }else{

        hanoi(n-1, x, z, y);//将x上编号为1至n-1的圆盘移到y,z作辅助塔

        moveAction(x, z);//将编号为n的圆盘从x移至z

        hanoi(n-1, y, x, z);//将y上编号为1至n-1的圆盘移到z,x作辅助塔

    }

}

4 显然,hanoi是一个递归函数,在函数的执行函数中,需要多次进行自我调用。那么,这个递归函数是如何进行的?与汇编程序设计中主程序和子程序之间的链接及信息交换相类似,在高级语言编制的程序中,调用函数和被调用函数之间的链接及信息交换需要通过栈来进行。

   通常,当在一个函数的运行期间调用另一个函数时,在运行被调用函数之前,系统需先完成3件事:

   * 将所有的实参,返回地址等信息传递给被调用函数保存

   * 为被调用函数的局部变量分配存储区

   * 将控制转移到被调用函数的入口

   而从被调用函数返回调用函数之前,系统也应完成3件工作:

   * 保存被调用函数的计算结果

   * 释放被调函数的数据区

   * 依照被调函数保存的返回地址将控制转移到调用函数

   当有多个函数构成嵌套调用时,按照“后调用先返回”的原则,上述函数之间的信息传递和控制转移必须通过“栈”来实现,即系统将整个程序运行时所需的数据空间安排在一个栈中,每当调用一个函数时,就为它在栈顶分配一个存储区,每当从一个函数退出时,就释放它的存储区,则当前正在运行的函数的数据区必在栈顶。

   一个递归函数的运行过程类似于多个函数的嵌套调用,只是调用函数和被调用函数是同一个函数,因此,和每次调用相关的一个重要的概念是递归函数运行的“层次”。假设调用该递归函数的主函数为第0层,则从主函数调用递归函数为进入第1层;从第i层递归调用本函数为进入“下一层”,即第i+1层。反之,退出第i层递归应返回至“上一层”,即第i-1层。为了保证递归函数正确执行,系统需设立一个“递归工作栈”作为整个递归函数运行期间使用的数据存储区。每一层递归所需信息构成一个“工作记录”,其中包括所有的实参,所有的局部变量以及上一层的返回地址。每进入一层递归,就产生一个新的工作记录压入栈顶。每退出一层递归,就从栈顶弹出一个工作记录,则当前执行层的工作记录必是递归工作栈栈顶的工作记录,称这个记录为“活动记录”,并称指示活动记录的栈顶指针为“当前环境指针”。

   实际上,在调用函数和被调用函数之间不一定传递参数的值,也可以传递参数的地址。通常,每个程序设计语言都有它自己约定的传递方法(包括被调用函数的执行结果如何返回调用函数等)。

   由于递归函数结构清晰,程序易读,而且它的正确性容易得到证明,因此,利用允许递归调用的语言进行程序设计时,给用户编制程序和调试程序带来很大的方便。因为对这样一类递归问题编程时,不需要用户自己而由系统来管理递归工作栈。

  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值