递归和迭代

归与迭代都是基于控制结构:迭代用重复结构,而递归用选择结构。递归与迭代都涉及重复:迭代显式使用重复结构,而递归通过重复函数调用实现重复。递归与迭代都涉及终止测试:迭代在循环条件失败时终止,递归在遇到基本情况时终止。使用计数器控制重复的迭代和递归都逐渐到达终止点:迭代一直修改计数器,直到计数器值使循环条件失败;递归不断产生最初问题的简化副本,直到达到基本情况。迭代和递归过程都可以无限进行:如果循环条件测试永远不变成false,则迭代发生无限循环;如果递归永远无法回推到基本情况,则发生无穷递归。

递归有许多缺点,它重复调用机制,因此重复函数调用的开销很大,将占用很长的处理器时间和大量的内存空间。每次递归调用都要生成函数的另一个副本(实际上只是函数变量的另一个副本).从而消耗大量内存空间。迭代通常发生在函数内,因此没有重复调用函数和多余内存赋值的开销。那么,为什么选择递归呢?

---------------------------------------------------------------------------摘要:在算法的分析与设计中,递归和迭代都是特别有力的工具,很多难解的问题都是通过递归或迭代算法解出来的。本文在比较这两种算法在不同情况下的可行性的基础上,阐述了怎样对这两种算法进行有效的选择。
关键词:递归算法 迭代算法 程序 
0 引言
在算法分析与设计中,递归与迭代是我们解决循环问题常用的两种方法。那么,在既可以用递归算法又可以用迭代算法解决的问题中,我们究竟该选用哪种算法呢?在程序设计中,我们不但讲求代码所能实现的功能,而且在实现相同功能的同时,更注重优化代码、提高代码的执行效率。这也是我们在选择递归还是迭代思想时考虑的主要因素。
1 递归和迭代概述
如果一个问题刚开始难以解决,可以将其简化后再尝试解决。如果这个过程可以重复进行,问题最终会变得容易处理。由此引出两种不同的方法:递归和迭代。循环或迭代,是一种重复执行一个过程的方法;递归是另一种方法。递归函数是通过调用函数自身来完成任务,而且在每次调用自身时减少任务量。而迭代是循环的一种形式,这种循环不是由用户输入而控制,每次迭代步骤都必须将剩余的任务减少;也就是说,循环的每一步都必须执行一个有限的过程,并留下较少的步骤。循环的进度通常用一个在每次迭代时都进行自增或自减的变量的值来衡量,直到到达预定的目标为止。用递归算法表示许多问题的求解方法时算法思想非常简洁。但是递归算法不仅时间效率非常差,而且由于递归算法是不断的函数调用和函数返回过程,因此其实际的计算机运行时间通常远大于循环方式算法的计算机运行时间,甚至在有限的时间内无法求解。这就存在一个把递归算法化为非递归算法的问题。
2 需要用迭代消解递归的情况 
递归算法特别适合于所研究的问题或所处理的数据本身是递归定义的情况。然而,并不意味着这种递归定义保证递归算法是解决该问题的最好方法。事实上,主要是因为拿那种不合适的例子来解释递归算法概念,从而造成了对程序设计中使用递归的普遍怀疑和否定态度,并把递归同低效等同起来。而且在递归算法中,往往会因为追求代码短或者在求解问题时一味追求规律性,多用了无用的压栈和出栈的操作。比如用循环消解的尾递归,是多了无用的压栈和出栈才使速度受损的;斐波那契数列计算的递归改循环迭代所带来的速度大幅提升,是因为改掉了重复计算的毛病。假使一个递归过程中本身包含了大量冗余的操作,并且这个过程又可以用迭代来达到相同的效果。这时,我们就一般用迭代来消解递归。也就是说尾递归算法和单向递归算法可用迭代算法来代替。可以用一个方案来描述人们力图在其中避免使用算法递归的程序,这个方案展示了其构成的模型。(1)式或等价的(2)式就是这个方案:
P≡ if B then (S;P)          (1)
P≡(S; if B then P)          (2)
要计算用简单递归关系定义的值时,这种方案是很自然的。如下例:
Function F (I: integer ):integer;
Begin if I>0 then F:=I*F(I-1)
      Else F:=1;
End                                    (3)
很明显,在这种情况下,递归可由简单迭代代替,即由下面的程序代替。
I:=0;F:=1;
While I<n do
Begin I:=I+1;F:=I*F
End (4)
一般地,对应于方案(1)或(2)的程序应按照下列方案改写:
P ≡(x:=x0;while B do S)
 还有更复杂的递归构造方案,这些方案可以并且应该改写成迭代计算的形式。一个例子就是Fibonacci 数的计算,这些数是按递归关系定义的:
 FIBn+1=FIBn+FIBn-1对n>0
 而FIB1=1,FIB0=0,一个直接的自然的解法导致程序
 function fib (n: integer): integer;
 begin if n=0 then fib :=0else
 if n=1 then fib :=1 else
 fib:=fib(n-1)+fib(n-2)
 end
以调用fib(n)来计算FIBn ,就引起这个函数过程的递归活动。频繁程度如何呢?我们注意到,对于n>1,每调用一次引起两个新的调。因此,调用的总次数按指数增长,如下图所示fib(5)的递归树。这样一个程序显然是不实用的。
但显见,利用辅助变量x=FIBi与y=FIBi-1,就可以用迭代方案来计算Fibonicca 数,而避免同一值的重复计算。
 { 对n>0计算x=FIBn}
i:=1;x:=1;y:=0;
while i<n do
    begin z:=x; i:=i+1;
         x:=x + y; y:=z
 end
(注意,对x, y, z的三个赋值,可以只表示成两个赋值,而不需要辅助变量z :x:=x +y ; y:=x-y).由上述例子可以看出,当存在明显的迭代解法时要避免使用递归。
3 不需要消解的递归
那种盲目的消解递归,不惜一切代价躲避递归,认为“递归的速度慢,为了提高速度,必须用栈或者其他的方法来消解”的说法是很片面的。如果一个递归过程用非递归的方法实现后,速度提高了,那只是因为递归做了一些无用功。假使一个递归过程必须要用栈才能消解,那么完全模拟后的结果根本就不会对速度有任何提升,只会减慢;如果你改完后速度提升了,那只证明你的递归函数写的有问题,如多了许多重复操作——打开关闭文件、连接断开数据库,而这些完全可以放到递归外面。可以在本质上是非递归的机器上实现递归过程这一事实本身就证明:为着实际目的,每一个递归程序都可以翻译成纯粹迭代的形式,但这包含着对递归栈的显式处理,而这些运算常常模糊了程序的本质,以致使它非常难以理解。
因此,是递归的而不是迭代的算法应当表述成递归过程。如汉诺塔问题等。汉诺塔问题的递归算法中有两处递归调用,并且其中一处递归调用语句后还有其他语句,因此该递归算法不是尾递归或单向递归。要把这样的递归算法转化为非递归算法,并没有提高程序运行的速度,反而会使程序变得复杂难懂,这是不可取的。也就是说,很多递归算法并不容易改写成迭代程序:它们本质上是递归的,没有简单的迭代形式。这样的递归算法不宜转化为非递归算法。
4 结束语
说到底,在我们选择算法时应该全面分析算法的可行性、效率、代码优化。在综合了算法的各个因素后,选择合适的算法来编写程序,这样的程序才会达到优化的效果。
参考文献:
[1]王晓东.算法分析与设计清华大学出版社.
[2]Alice E. Fischer DavidW .Egger 等著Applied C: An Introduction and More电子工业出版社.

 

本文来自优快云博客,转载请标明出处:http://blog.youkuaiyun.com/liuzongshun/archive/2009/05/22/4209420.aspx

<think>好的,用户想了解递归迭代算法之间的主要差异及适用场景。首先,我需要回忆一下已有的知识,并结合提供的引用内容来构建回答。 首先,递归迭代都是控制结构,但实现方式不同。递归是通过函数自身调用来解决问题,而迭代则使用循环结构。根据引用[1],递归迭代各有优缺点,适用于不同的场景。引用[3]提到递归适合自相似性、分治或树形结构的问题,而迭代适合大规模数据或性能要求高的场景。 接下来,我需要明确两者的差异点。根据引用[5],递归必须有一个基本情况作为终止条件,否则会导致无限递归。而迭代则通过循环条件来控制,可能更容易出现无限循环,但通常结构更简单。 在性能方面,引用[4]指出递归在n较大时执行时间远高于迭代,因为递归需要重复计算子问题,比如斐波那契数列的递归实现会有大量重复计算。而迭代可以通过存储中间结果来优化,比如动态规划。 内存使用方面,递归的堆栈开销较大,可能导致栈溢出,而迭代通常使用固定内存,更高效。例如,处理深度大的树结构时,递归可能有风险,这时迭代更安全。 适用场景方面,递归适合问题分解自然的场景,如树的遍历、分治算法迭代适合线性处理,如数组遍历、数值计算。 需要结合引用中的例子,比如引用[3]提到的树形结构适合递归,大规模数据适合迭代。同时引用[4]中的斐波那契数列例子说明递归的效率问题。 最后,总结差异选择建议,帮助用户根据具体问题选择合适的方法。确保回答结构清晰,分点说明,并引用相关文献支持。</think>### 递归迭代算法的主要差异及适用场景 #### 一、核心差异对比 1. **实现方式** - **递归**:通过函数自身调用分解问题,需定义明确的**基本情况(Base Case)**作为终止条件[^5]。例如阶乘计算: $$n! = n \times (n-1)! \quad (\text{当 } n=0 \text{ 时 } 0! = 1)$$ - **迭代**:使用循环结构(如`for`、`while`)重复执行代码块,通过循环变量控制过程。例如求计算: ```python sum = 0 for i in range(1, n+1): sum += i ``` 2. **性能与资源消耗** - **递归**:存在**重复计算****堆栈溢出风险**。例如斐波那契数列的递归实现会重复计算子问题,时间复杂度为$O(2^n)$[^4]。 - **迭代**:通常通过变量存储中间结果,时间复杂度更低(如斐波那契数列迭代实现为$O(n)$),且内存占用更稳定。 3. **代码可读性与维护性** - **递归**:代码简洁,更贴近数学定义(如汉诺塔问题)。 - **迭代**:逻辑更直接,适合线性流程(如数组遍历)。 --- #### 二、适用场景分析 | **场景特点** | **推荐方法** | **示例** | |----------------------|--------------|------------------------------| | 自相似性/分治结构 | 递归 | 快速排序、二叉树遍历 | | 树形/图结构处理 | 递归 | DOM树解析、迷宫路径搜索 | | 大规模数据/高性能需求 | 迭代 | 图像处理、数值模拟 | | 简单线性流程 | 迭代 | 数组求、线性搜索 | --- #### 三、选择建议 1. **优先选择递归**:当问题天然适合递归分解(如分治算法、回溯算法)且数据规模较小时。 2. **强制使用迭代**:当存在堆栈溢出风险(如深度超过$10^4$的递归)或需要严格性能优化时[^3]。 3. **混合策略**:部分场景可结合两者优势,例如通过**迭代实现深度优先搜索(DFS)**来避免递归堆栈限制。 --- #### 示例代码对比 **递归实现斐波那契数列**(简洁但低效): ```python def fib(n): if n <= 1: return n return fib(n-1) + fib(n-2) # 重复计算导致O(2^n)复杂度 ``` **迭代实现斐波那契数列**(高效): ```python def fib(n): a, b = 0, 1 for _ in range(n): a, b = b, a + b return a # 时间复杂度O(n) ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值