是否有任何通用的启发式,提示,技巧或常用设计范例可用于将递归算法转换为迭代算法?我知道可以做到,我想知道在这样做时是否值得记住这些做法。
答案 0 :(得分:30)
您通常可以完全保留递归算法的原始结构,但通过使用尾调用并更改为延续传递来避免堆栈,如this blog entry所示。 (我应该做一个更好的独立示例。)
答案 1 :(得分:22)
我在使用迭代算法替换递归算法的过程中使用的常用技术通常是使用堆栈,推送传递给递归函数的参数。
检查以下文章:
答案 2 :(得分:8)
通常的做法是来管理LIFO堆栈,该堆栈保留“仍有待完成”的运行列表,并在while循环中处理整个过程,该循环一直持续到列表为空。
使用这种模式,真正的递归模型中的递归调用将被替换为
- 将当前(部分完成)任务的“上下文”推入堆栈,
- 将新任务(提示递归的任务)推送到堆栈上
- 并且“继续”(即跳转到while循环的开头)。
在循环的头部附近,逻辑弹出最近插入的上下文,并在此基础上开始工作。
实际上这只是“移动”信息,否则这些信息将被保存在“系统”堆栈上的嵌套堆栈帧中,并移动到应用程序管理的堆栈容器中。然而,这是一种改进,因为这个堆栈容器可以分配到任何地方(递归限制通常与“系统”堆栈中的限制相关联)。因此,基本上完成相同的工作,但是“堆栈”的显式管理允许这发生在单个循环结构中而不是递归调用中。
答案 3 :(得分:7)
通常,递归可以通过尾递归替换,通过在累加器中收集部分结果并使用递归调用将其传递下来。尾递归本质上是迭代的,递归调用可以实现为跳转。
例如,factorial
的标准一般递归定义factorial(n) = if n = 0 then 1 else n * factorial(n - 1)
可以替换为
factorial(n) = f_iter(n, 1)
和
f_iter(n, a) = if n = 0 then a else f_iter(n - 1, n * a)
这是尾递归。它与
相同a = 1;
while (n != 0) {
a = n * a;
n = n - 1;
}
return a;
答案 4 :(得分:4)
查看这些链接以获取性能示例
Recursion VS Iteration (Looping) : Speed & Memory Comparison
和
Replace Recursion with Iteration
和
问:通常是递归版本 快点? 答:不 - 它通常较慢(由于维护的开销 堆栈)
Q: Does the recursive version usually use less memory? A: No -- it usually uses more memory (for the stack). Q: Then why use recursion?? A: Sometimes it is much simpler to write the recursive version (but
我们需要等到我们 讨论树木,看到真的很好 实例...)
答案 5 :(得分:4)
我通常从基本情况开始(每个递归函数都有一个)并向后工作,如果需要,将结果存储在缓存(数组或哈希表)中。
您的递归函数通过解决较小的子问题并使用它们来解决问题的实例来解决问题。每个子问题也被进一步细分,依此类推,直到子问题太小,解决方案很简单(即基本情况)。
我们的想法是从基础案例(或基本案例)开始,并使用它来构建更大案例的解决方案,然后使用它们构建更大的案例等等,直到整个问题得到解决。这不需要堆栈,可以使用循环来完成。
一个简单的例子(在Python中):
#recursive version
def fib(n):
if n==0 or n==1:
return n
else:
return fib(n-1)+fib(n-2)
#iterative version
def fib2(n):
if n==0 or n==1:
return n
prev1,prev2=0,1 # start from the base case
for i in xrange(n):
cur=prev1+prev2 #build the solution for the next case using the previous solutions
prev1,prev2=cur,prev1
return cur
答案 6 :(得分:3)