递归本质讨论

source: click here


[递归]-分- [递推] 和 [回归]
递归的概念及递归算法的结构
1、所谓的递归,是指函数在执行过程中自己调用了自己或者说某种数据结构在定义时又引用了自身。这两种情况都可理解为递归。比如:
void fun()
{
..
fun()
..
}//fun
以上函数fun就是一个递归函数。而针对于各种数据结构中的递归结构就更多了,如单链表,广义表,树。在这些递归结构中,具有一个相同的特征:其中的某个域的数据类型是其结点类型本身!

2、递归算法的大致结构为:
a、递归出口
b、递归体

一个递归算法,当其问题求解的规模越来越小时必定有一个递归出口,就是不再递归调用的语句。递归体则是每次递归时执行的语句序列。比如以下简要描述的递归函数中:
f(n)=1 (当n=0时)
f(n)=n*f(n-1) (当n>0时)
这个递归函数,实际是求n的阶乘。当n=0时,不再递归调用,而当其值置为1;当n>0时,就执行n*f(n-1),这是递归调用。从整体上理解递归算法的大致结构有利于我们在设计递归算法时,从总体上把握算法的正确性。

二、栈与递归的关系:递归的运行
递归在实现过程中是借助于栈来实现的。高级语言的函数调用,每次调用,系统都要自动为该次调用分配一系列的栈空间用于存放此次调用的相关信息:返回地址,局部变量等。这些信息被称为工作记录(或活动记录)。而当函数调用完成时,就从栈空间内释放这些单元,但是,在该函数没有完成前,分配的这些单元将一直保存着不被释放。递归函数的实现,也是通过栈来完成的。在递归函数没有到达递归出口前,都要不停地执行递归体,每执行一次,就要在工作栈中分配一个工作记录的空间给该“层”调用存放相关数据,只有当到达递归出口时,即不再执行函数调用时,才从当前层返回,并释放栈中所占用的该“层”工作记录空间。请大家注意,递归调用时,每次保存在栈中的是局部数据,即只在当前层有效的数据,到达下一层时上一层的数据对本层数据没有任何影响,一切从当前调用时传过来的实在参数重新开始。
由此可见,从严老师P版教材中,利用栈将递归向非递归转化时所采用的方法,实质是用人工写的语句完成了本该系统程序完成的功能,即:栈空间中工作记录的保存和释放。大家在以后的作题时,可以参照以上的分析来理解递归函数的运行过程。实际上,现在的考试中,已经很少见到有学校要求运用栈与实现递归转化为非递归来解题了,所以,大家能理解这个算法更好,不能理解的也不用太担心。我曾就此问题专门向严老师咨询过,严老师说之所以在C版的教材中没有讲到这个算法,也是考虑到了目前国内学校在这方面已经基本不作要求。但是,递归算法的运行过程应该心中有数。

三、递归与递推的关系
“递归算法的执行过程分递推与回归两个阶段。在递推阶段,把较复杂的问题(规模为n)的求解推到比原问题简单一些的问题(规模小于n)的求解。在回归阶段,当获得最简单的情况后,逐级返回,依次获得稍复杂问题的解。”(摘自于“高程”教材)
“递推法是利用问题本身所具有的一种递推关系求问题解的一种方法。设要求问题规模为N的解,当N=1时,解或已知,或能非常方便地得到解。能采用递推法构造算法的问题有重要的递推性质,即当得到问题规模为i-1的解后,由问题的递推性质,能从已求得的规模为1,2,3、、、i-1的一系列解,构造出问题规模为i的解。直到最终得到问题规模为N的解。”
由此可见,递推是递归的一个阶段,递归包含着递推。当然,对于实际的算法设计,知不知道这两者之间的关系并不重要,重要的是我们能找出这其中的递推规律和回归时机。

四、适合于用递归实现的问题类型
必须具有两个条件的问题类型才能用递归方法求得:
1、规模较大的一个问题可以向下分解为若干个性质相同的规模较小的问题,而这些规模较小的问题仍然可以向下分解。
2、当规模分解到一定程度时,必须有一个终止条件,不得无限分解。
由此可见适合于递归实现的问题类型有:
1、函数定义是递归的。如阶乘,FIB数列。
2、数据结构递归的相关算法。如:树结构。
3、解法是递归的。如:汉诺塔问题。

五、递归算法的设计
从递归算法的结构来分析,进行递归算法的设计时,无非要解决两个问题:递归出口和递归体。即要确定何时到达递归出口,何时执行递归体,执行什么样的递归体。递归算法算法设计的关键是保存每一层的局部变量并运用这些局部变量。由此,递归算法的设计步骤可从以下三步来作:
1、分析问题,分解出小问题;
2、找出小问题与大问题之间的关系,确定递归出口;
3、用算法语言写出来。


六、递归算法向非递归算法的转化方法
1、迭代法
如果一个函数既有递归形式的定义,又有非递归的迭代形式的定义,则通常可以用循环来实现递归算法的功能。
2、消除尾递归
尾递归,是一类特殊的递归算法。它是指在此递归算法中,当执行了递归调用后,递归调用语句后面再没有其它可以执行的语句了,它即没有用到外层的状态,也没有必要保留每次的返回地址,因为其后不再执行其它任何*作,所以可以考虑消除递归算法。这种情况下,我们可以用循环结构设置一些工作单元来帮助消除尾递归,这些工作单元用于存放一层层的参数。
3、利用栈
当一个递归算法不利于用迭代法和消除尾递归法实现向非递归算法的转化时,可以考虑用栈来实现。实现的过程实际上就是用人工的方法模拟系统程序来保存每层的参数,返回地址,以及对参数进行运算等。
一般情况下,对于递归算法向非递归算法的转化问题,特别是结构定义时的递归算法,我们通常先写出递归算法,然后再向非递归算法转化,而不是首先就尝试写出非递归算法来。


------------------------------------------------------------------------------------------------------------------------

如果运用列表来形容归纳法就是:

步进表达式:问题蜕变成子问题的表达式

结束条件:什么时候可以不再是用步进表达式

直接求解表达式:在结束条件下能够直接计算返回值的表达式

逻辑归纳项:适用于一切非适用于结束条件的子问题的处理,当然上面的步进表达式其实就是包含在这里面了。

这样其实就结束了,递归也就出来了。

递归算法的一般形式:  
    void   func( mode){  
          if(
endCondition){  
                 
constExpression       //
基本项 
          }  
          else

{

accumrateExpreesion /归纳项

mode=expression //步进表达式

func(mode) / /调用本身,递归

}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值