zoukankan      html  css  js  c++  java
  • 尾递归

    尾递归

      如果一个函数中所有递归形式的调用都出现在函数的末尾,我们称这个递归函数是尾递归的。当递归调用是整个函数体中最后执行的语句且它的返回值不属于表达式的一部分时,这个递归调用就是尾递归。尾递归函数的特点是在回归过程中不用做任何操作,这个特性很重要,因为大多数现代的编译器会利用这种特点自动生成优化的代码。

      当编译器检测到一个函数调用是尾递归的时候,它就覆盖当前的活跃记录而不是在栈中去创建一个新的。编译器可以做到这点,因为递归调用是当前活跃期内最后一条待执行的语句,于是当这个调用返回时栈帧中并没有其他事情可做,因此也就没有保存栈帧的必要了。通过覆盖当前的栈帧而不是在其之上重新添加一个,这样所使用的栈空间就大大缩减了,这使得实际的运行效率会变得更高。因此,只要有可能我们就需要将递归函数写成尾递归的形式。

      为了理解尾递归是如何工作的,让我们再次以递归的形式计算阶乘。首先,这可以很容易让我们理解为什么之前所定义的递归不是尾递归。回忆之前对计算n!的定义:在每个活跃期计算n倍的(n-1)!的值,让n=n-1并持续这个过程直到n=1为止。这种定义不是尾递归的,因为每个活跃期的返回值都依赖于用n乘以下一个活跃期的返回值,因此每次调用产生的栈帧将不得不保存在栈上直到下一个子调用的返回值确定。现在让我们考虑以尾递归的形式来定义计算n!的过程。函数可以定义成如下形式:

      

      这种定义还需要接受第二个参数a,除此之外并没有太大区别。a(初始化为1)维护递归层次的深度。这就让我们避免了每次还需要将返回值再乘以n。然而,在每次递归调用中,令a=na并且n=n-1。继续递归调用,直到n=1,这满足结束条件,此时直接返回a即可。图3-4说明了用尾递归计算4!的过程。注意在回归的过程中不需要做任何操作,这是所有尾递归函数的标志。

      

      代码实例3-2给出了一个C函数facttail,它接受一个整数n并以尾递归的形式计算n的阶乘。这个函数还接受一个参数a,a的初始值为1。facttail使用a来维护递归层次的深度,除此之外它和fact很相似。读者可以注意一下函数的具体实现和尾递归定义的相似之处。

     1 /* facttail.c */                                                                          
     2 #include "facttail.h"  
     3 /* facttail  */  
     4 int facttail(int n, int a) {  
     5 /* Compute a factorial in a tail-recursive manner. */  
     6 if (n < 0)  
     7    return 0;  
     8 else if (n == 0)  
     9    return 1;  
    10 else if (n == 1)  
    11    return a;  
    12 else  
    13    return facttail(n - 1, n * a);  
    14 } 

    参考:http://book.51cto.com/art/201212/370096.htm

  • 相关阅读:
    IT项目中使用 json格式数据 保存项目配置信息, 在配置文件再读取json文件的内容进行赋值
    python 使用eval() 可以将json格式的数据,转换为原始数据
    python 取出字典的键或者值/如何删除一个字典的键值对/如何遍历字典
    python2 中 unicode 和 str 之间的转换及与python3 str 的区别
    python 将列表嵌套字典的unicode字符串转换为str格式的字符串的方法
    Django 项目拆分配置文件settings.py
    苹果笔记本调整 pycharm 字体大小的地方
    django rest framework 向数据库中插入数据时处理外键的方法
    java实现文件的压缩和解压
    java 跨数据库导入大数据
  • 原文地址:https://www.cnblogs.com/tekkaman/p/3754669.html
Copyright © 2011-2022 走看看