递归算法探讨

递归算法探讨

递归在计算机科学和数学中是一个很重要的工具它在程序设计语言中用来定义句法在数据结构中用来解决表或树形结构的搜索和排序等问题。另外递归在计算方法、运筹学模型、行为策略和图论的研究中都得到了广泛的应用。 

1、 递归的概念

若一个对象部分地包含它自己或用它自己给自己定义则称这个对象是递归的在程序设计中若一个过程直接地或间接地调用自己则称这个过程是递归的过程。在定义一个过程或函数时出现了调用本过程或函数的成分即调用自己本身称之为直接递归若过程或函数P调用过程或函数Q , 而 调用P, 称之为间接递归。对于问题定义是递归的数据结构是递归的问题解法是递归的3种情况都可以采用递归方法来处理。

2、 递归算法的本质

递归算法的本质是把一个大型复杂的问题层层转化为若干与原问题相似的规模较小的问题来处理当规模小到一定程度时可以直接得出它的解这样通过递推就可得到原来问题的解。递归调用的次数必须是有限的必须有递归结束的条件。递归算法的执行过程分为两步第一步是从目标出发追溯到源头称为回溯。第二步是从源头逐步回代达到目标称为递推。由于存在递推在回溯时必须保留其返回的地址与参数使程序能够返回到调用处继续执行这一步是系统通过设置栈来实现的程序设计者无需对栈进行管理

3递归算法的设计

适宜用递归算法求解的问题的充要条件是问题具有某种可借用的类同自身的子问题描述的性质某一有限步的子问题有直接的解存在。递归算法的设计通常有以下个步骤

1分析问题设计递归公式将一个问题化解为一个或多个子问题求解且子问题和原问题具有相同的解法。

2设计递归结束条件控制递归,递归最后一级的调用必须不能再进行递归。 

3确定参数设计递归函数递归过程或递归函数的参数值在递归过程中必须是按规律变化的且参数值的增减方向应与递归终止条件相匹配这样才能控制递归调用。一般递归函数设计的格式为 

if (递归结束条件 

return (结束递归时的返回值)

else

return (递归表达式)

4递归算法的实例

1: 用递归函数编程求的阶乘n!。阶乘函数的递归定义如下: n! =n×(n-1)!  (n> 0)这种定义方法是用阶乘函数自身定义了阶乘函数。由于n!(n-1)!都是同一个问题的求解因此可将n!用递归函数来描述。

程序代码如下

Long f( int n) {

 if ( n = = 0 )

 return 1; //递归的终止条件及相应的操作 

else

 return f (n-1) ; //递归调用 

2: 中序遍历二叉树的递归算法。 

void Inorder ( BTreeNode BT ) {

 if ( BT ! = NULL ) {

 Inorder ( BT - > lchild) ;

 Visit (BT ) ;

 Inorder ( BT - > rchild) ; } } 

5递归算法的执行过程分析

递归的执行依赖系统堆栈的支持递归的执行过程主要分为两步回溯(逐层深入递归调用和递推(层层向上递归返回) , 在回溯时需要做的工作有进行断点保存局部变量、形式参数保存。控制流程转向递归调用的入口。

在本次递归调用结束后向上层调用返回时需要做的工作有保存本次调用的函数结果恢复调用函数时的局部变量和形式参数。根据递归调用时将控制流程转回到调用函数中递归调用的下一行代码处继续执行。

综上所述递归算法的执行过程是不断地自调用直到到达递归出口才结束自调用过程;到达递归出口后,递归算法开始按最后调用的过程最先返回的次序返回;返回到最外层的调用语句时递归算法执行过程结束。

6递归算法的非递归化

递归算法在执行时存在多次进栈和出栈流程的跳转和返回甚至会出现多次重复计算从而影响执行效率。还有一些高级程序设计语言没有提供递归的机制和手段。因此有些时候将递归算法非递归化是有必要的。非递归化最重要的是理解递归的执行过程。对于一般的递归算法,可以利用以下两种方法对其进行非递归化。

1、 尾递归的非递归化

如果递归调用语句是函数的最后一条执行语句,则称这种递归调用为尾递归。当递归调用进入内层时,外层上与各形式参数对应的实际参数值和返回地址都会被编译系统自动保存下来,以备返回时使用。对于尾递归,调用返回时,其后已没有执行语句了。因而外层的实际参数值不会再用到,故没有必要保留。此外由于递归调用语句是最后一条可执行语句,返回地址肯定在函数末尾,故其返回地址也没有必要保留下来。对于这种情况,关键是从递归调用出发,从上而下递归到底找到递归的终止条件然后用循环实现递归算法的非递归化。

3: 的阶乘n!的递归算法的非递归化。

1中给出了求n的阶乘n!的递归算法从上而下递归:

 f(n) = nf(n-1) = n(n-1)f(n-2) = n(n-1) (n- 2) f (n- 3) : : f (0)

 f (0) = 1

设最终结果用f表示由递归的终止条件“n=0结果为1”, f的初始值= 1。由此,可从下而上地用循环实现求f(i),其中i1到任意正整数n,f随着i的变化而变化其非递归算法如下

Long f(int n) {

int i;

long f = 1; 

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

f = f*i

return f; } 

类似的情形很多如求2个正整数的最大公约数和求Fibonacci数列等。

2、 非尾递归的非递归化

如果递归调用语句不是函数中的最后一个语句则称该递归调用为非尾递归。对于非尾递归调用中的入口地址计算机隐含地自动设置堆栈保留调用入口地址供递归返回使用。而用非递归方法堆栈是人为设定显现在程序中功能与递归算法相同。

4: 中序遍历二叉树。

由二叉树的递归算法的执行过程知在二叉树非空时首先访问根的左子树再访问根最后访问根的右子树访问根的左子树时先要访问左子树根的左子树再访问左子树的根其次再访问左子树根的右子树⋯⋯如此递归下去一直到树的最左下结点被访问(向左下搜索时将当前结点压入系统栈中保存以便向上回退时调出) , 然后访问最左下结点的父结点通过弹栈获得最左下结点的父结点然后处理该父结点的右子树以此类推循环直到整棵树访问完毕。根据上述对递归执行过程的分析其对应的非递归算法为

void Inorder ( BTreeNode BT ) 

{

if ( ! BT ) return ;

Stack S = Init-Stack () ; 

while ( ! Empty-Stack(S) ) 

 while(BT) //当指针BT非空时入栈 

{Push (S,BT);

   BT = BT-> lchild;} 

Pop(S,BT) ; 

Visit(BT);

BT = BT->rchild; 

//end while (Empty-Stack ( s) ) 

}//end InOrder () 

7结束语

递归算法具有代码简洁思路清晰的优点是设计算法的强有力工具。一般而言递归程序的执行效率低于非递归程序但非递归算法往往难于编写容易出错理论上递归算法都可以转化为非递归算法但存在一些算法很难非递归化如复杂的间接递归因此要根据问题需求及软件和硬件的环境等具体情形选择递归还是非递归。

你可能感兴趣的:(数据结构与算法)