C语言入门(12)——递归


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

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

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


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

完整的代码如下:

#include <stdio.h>
long factorial(intn)
{
    if(n== 1 || n == 0 )
    {
        return1;
    }
    else
    {
        returnfactorial(n - 1) * n;/*递归调用 */
    }
}
 
int main(void)
{
    intn;
    printf("请输入正整数:
");
    scanf("%d",&n);
    printf( "%d的阶乘是:%d
", 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