博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
C语言入门(12)——递归
阅读量:5164 次
发布时间:2019-06-13

本文共 2571 字,大约阅读时间需要 8 分钟。

一个函数在它的函数体内调用它自身称为递归调用。有递归调用操作的函数被称为递归函数。递归调用可以是直接调用,也可以是间接调用。也可以理解为函数的嵌套调用是函数本身。

例如实现一个求阶乘的函数:

long factorial(intn){    if(n== 1 || n == 0 )    {        return1;    }    else    {        returnfactorial(n - 1) * n;/*递归调用 */    }}

这个函数是一个递归函数。但是运行该函数将无休止地调用其自身,这当然是不正确的。为了防止递归调用无终止地进行,必须在函数内有终止递归调用的手段。常用的办法是加条件判断,满足某种条件后就不再作递归调用,然后逐层返回。

完整的代码如下:

#include 
long factorial(intn){ if(n== 1 || n == 0 ) { return1; } else { returnfactorial(n - 1) * n;/*递归调用 */ }} int main(void){ intn; printf("请输入正整数:\n"); scanf("%d",&n); printf( "%d的阶乘是:%d\n", n,factorial(n)); system("pause"); return0;}

运行结果:

 

在C语言中任何函数之间不能嵌套定义, 主调函数与被调函数之间相互独立(彼此可以调用)。发生函数调用时,被调函数中保护了主调函数的运行环境和返回地址,使得调用函数的状态可以在被调函数运行返回后完全恢复,而且该状态与被调函数无关。  

被调函数运行的代码虽是同一个函数的代码体,但由于调用点,调用时状态,返回点的不同,可以看作是函数的一个副本,与调用函数的代码无关,所以函数的代码是独立的。被调函数运行的栈空间独立于调用函数的栈空间,所以与调用函数之间的数据也是无关的。函数之间靠参数传递和返回值来联系,函数看作为黑盒。这种机制决定了C/C++允许函数递归调用

递归调用分为两种形式:直接递归和间接递归。

直接递归即在函数中出现调用函数本身。

例如,下面的代码求斐波那契数列第n项。 斐波那契数列的第一和第二项是1,后面每一项是前二项之和,即1,1,2,3,5,8,13,...。代码中采用直接递归调用:  

long fib(intn){    if( n > 2 )    {        return(fib(n - 1) + fib(n - 2) );/*直接递归*/    }    else    {        return1;    }}

 

间接递归调用是指函数中调用了其他函数,而该其他函数却又调用了本函数。例如,下面的代码定义两个函数,它们构成了间接递归调用:  

int func1(intx){    if( x <= 0 )    {        returnx;    }    returnfunc2( x );} int func2(inty){    returnfunc1( y - 1 );}

在上面的例子中,func1()函数调用了func2()函数,而func2()函数又调用了func1()函数。由于他们都互相调用,因此又被称为互递归函数

 

递归的条件有4点:

1、必须有完成函数任务的语句,也就是停止递归的条件。

 

2、必须有能够确定是否能避免递归调用的测试,比如求阶乘的函数:

 

long factorial(intn){    if(n== 1 || n == 0 )    {        return1;    }    else    {        returnfactorial(n - 1) * n;/*递归调用 */    }}

    语句" if(n== 1 || n == 0 )"就是—个测试, 如果不满足条件,就不进行递归调用。  

3、一个递归调用语句。该递归调用语句的参数应该逐渐逼近不满足条件,以至最后断绝递归。

    例如,上面求阶乘代码中,语句“factorial(n - 1)” 便是一个递归调用,参数在渐渐变小,这种发展趋势能使测试"if(val>1)”最终不满足。

4、必须先测试,后递归调用。在递归函数定义中,必须先测试,后递归调用。也就是说,递归调用是有条件的,满足了条件后,才可以递归。否则这个函数就会永远调用下去,直到系统为程序预留的栈空间耗尽程序崩溃为止,这称为无穷递归。

例如,下面的代码是一个无穷递归函数:

int func(intx){    inty,z;    z= func(y);    returnz;}
大多数递归函数都能用非递归函数来代替,这种替代操作叫做消去递归。下面例子的代码求两个整数a,b的最大公约数,分别用递归和非递归函数实现

 

/*递归实现*/long gcdt(inta,intb){    if( a % b== 0 )        returnb;    returngcdl( b, a% b );}/*非递归实现*/long gcd2(inta,intb){    inttemp;    while(b!= 0)    {        temp= a%b;        a= b;        b= temp;    }    returna;}
递归和循环是等价的,用循环能做的事用递归都能做,反之亦然

   

对递归的评价:

1、递归的目的是简化程序设计,使程序易读。

2、但递归增加了系统开销。 时间上, 执行调用与返回的额外工作要占用CPU时间。空间上,随着每递归一次,栈内存就多占用一截。

3、相应的非递归函数虽然效率高,但却比较难编程,而且相对来说可读性差。

4、现代程序设计的目标主要是可读性好。随着计算机硬件性能的不断提高,程序在更多的场合优先考虑可读而不是高效,所以,鼓励用递归函数实现程序思想。

 

转载于:https://www.cnblogs.com/new0801/p/6177140.html

你可能感兴趣的文章
tableView
查看>>
Happy Great BG-卡精度
查看>>
Xamarin Visual Studio不识别JDK路径
查看>>
菜鸟“抄程序”之道
查看>>
Ubuntu下关闭防火墙
查看>>
TCP/IP 邮件的原理
查看>>
原型设计工具
查看>>
windows下的C++ socket服务器(4)
查看>>
css3 2d转换3d转换以及动画的知识点汇总
查看>>
【Java】使用Eclipse进行远程调试,Linux下开启远程调试
查看>>
对Vue为什么不支持IE8的解释之一
查看>>
计算机改名导致数据库链接的诡异问题
查看>>
Java8内存模型—永久代(PermGen)和元空间(Metaspace)(转)
查看>>
ObjectiveC基础教程(第2版)
查看>>
centos 引导盘
查看>>
Notes of Daily Scrum Meeting(12.8)
查看>>
Apriori算法
查看>>
onlevelwasloaded的调用时机
查看>>
求出斐波那契数组
查看>>
lr_start_transaction/lr_end_transaction事物组合
查看>>