zoukankan      html  css  js  c++  java
  • 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、现代程序设计的目标主要是可读性好。随着计算机硬件性能的不断提高,程序在更多的场合优先考虑可读而不是高效,所以,鼓励用递归函数实现程序思想。

  • 相关阅读:
    linux 下spyder安装
    【C++】fill函数,fill与memset函数的区别
    【tensorflow使用笔记一】:安装linux下tensorflow环境的问题
    leetcode 49 字母异位词分组
    leetcode 1014. 在 D 天内送达包裹的能力
    【C++进阶:STL常见性质3】
    【C++进阶:STL常见性质2】
    【C++进阶:STL常见性质】
    【C++进阶:移位运算符的用法】
    面向对象之静态方法
  • 原文地址:https://www.cnblogs.com/new0801/p/6177140.html
Copyright © 2011-2022 走看看