开篇综述!关于递归与尾递归!

本文详细介绍了阶乘计算中的线性递归与尾递归实现方式,通过比较两者在计算过程、资源消耗及效率上的区别,阐述了尾递归相较于线性递归的优势,并解释了尾递归如何通过避免栈溢出优化计算过程。
 转至  http://www.cppblog.com/superKiki/archive/2010/08/09/122869.html

下面两个程序是C写的计算阶乘的递归和尾递归实现
线性递归:
long factorial(long n)    
{     
  return(n == 1) ? 1 : n * factorial(n - 1);     
}     

尾递归:
long fact_iter(long product, long counter, long maxcount)
{     
  return (counter > maxcount) ? product : fact_iter(product*counter, counter+1, maxcount);
}     

long factorial(long n)
{
  return fact_iter(1, 1, n);   
}    

线性递归程序基于阶乘的递归定义,即:对于一个正整数n,n!就等于n乘以(n-1)!:
n! = n[(n-1)(n-2)…3*2*1] =n(n-1)!
程序一在计算4!时的过程是这样的:
(factorial 4)
(* 4 (factorial 3))
(* 4 (* 3 (factorial 2)))
(* 4 (* 3 (* 2 (factorial 1))))
(* 4 (* 3 (* 2 1)))
(* 4 (* 3 2))
(* 4 6)
24

而尾递归程序用的是另一种计算规则,即先用1乘以2,再将得到的结果乘以3,再乘以4,这样下
去直到n。程序中维持着一个变动中的乘积product,以及一个从1到n的计数器counter,这一
计算过程中的product和counter在每一步都按照下面规则改变:
product = counter * product
counter = counter + 1
这样循环下去,可以得到n!也就是计数器counter超过n时乘积product的值。
所以程序二在计算4!的过程是这样的:
(factorial 4)
(fact-iter 1 1 4)
(fact-iter 1 2 4)
(fact-iter 2 3 4)
(fact-iter 6 4 4)
(fact-iter 24 5 4)
24

一个对自己本身的递归尾调用,就叫做尾递归。这里尾调用的“尾”字,是指运行
时需要执行的最后一个动作。不是简单的语法字面上的最后一个语句。 尾递归实
际执行的是迭代的计算过程。
线性递归函数必须满足以下两个基本属性:
*必须清晰无误地解决基的情况。
*每一个递归的调用,必须包含更小的参数值。
而尾递归则不必满足这两个条件。
普通的线性递归比尾递归更加消耗资源, 在实现上说, 每次重复的过程调用都使得调
用链条不断加长. 系统不得不使用栈进行数据保存和恢复.而尾递归就不存在这样的问
题, 因为它的状态完全由函数的参数保存. 并且,由于尾递归的函数调用出现在调用者
函数的尾部,因为是尾部,所以根本没有必要去保存任何局部变量,sp, pc的信息。
直接让被调用的函数返回时越过调用者,返回到调用者的调用者去。
尾调用优化不是什么很复杂的优化,实际上几乎所有的现代的高级语言编译器都支持
尾调用这个很基本的优化。实现层面上,只需要把汇编代码call改成jmp, 并放弃所有
局部变量压栈处理,就可以了。这样一来,堆栈根本就没有被占用,每次调用都是重
新使用调用者的堆栈。
尽管尾递归比递归高效,但并非所有的递归算法都可以转成尾递归的,因为尾递归本质
上执行的是迭代的计算过程。这与并非所有的递归算法都可以转成迭代算法的原因是一样的

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值