在递归中,所有内容都在堆栈中处理,迭代(循环)是否也在堆栈中处理?
如果我们深入研究它们,递归的时间和空间复杂度会根据条件而增加,而迭代时间复杂度随着空间复杂度的增加而保持不变。
任何人都可以更深入地澄清这个话题。
答案 0 :(得分:3)
不,他们是不同的概念。
可能(并且很可能)在实际代码中组合这两个概念。
例如,要遍历一些树结构(JSON或XML),您通常会得到一些像这样的函数(伪代码):
function parseNode(node) {
// process this node
switch (node.type) {
// ...
}
foreach (child in node.children) // iterate over all child nodes
parseNode(child) // this is the recursive call
}
假设您有以下具有节点的树:
A
|
B-+-C
| |
E F-+-G
要开始解析树,您需要调用parseNode(A)
。
这将按以下顺序触发处理:A,B,E,C,F,G
正如您所看到的,这将通过分支走树枝。当向下一步/深度时,你有一个递归调用。每当你切换到下一个兄弟时,你都在迭代。
答案 1 :(得分:2)
迭代不是递归。但是,有一种称为尾递归的递归特殊情况,可以对迭代进行优化。当函数的最后一个操作再次调用该函数时,会发生尾递归。在这种情况下,不需要在堆栈上保存任何内容,编译器只需将跳转插入函数的开头即可。
以下是更多详情What is tail recursion?
此外,如果你想深入了解这个主题。阅读这本优秀的书。 http://mitpress.mit.edu/sicp/full-text/book/book.html
答案 2 :(得分:1)
运行recursion
与iteration
的代码之间在时间/空间复杂性方面的主要差异是由此引起的:
当recursion
运行时,它将为每个递归调用创建新的stack frame。由于局部变量,调用者的地址等,每个这样的帧消耗额外的内存。
当递归到达终点时,所有这些帧都将开始展开。
因此,递归往往会消耗更多的堆栈内存,并且当其运行时深度未知且可能消耗为当前线程分配的所有堆栈内存时会很危险,在这种情况下,您将拥有{{3} }:)
迭代没有这个问题,它们消耗了不断数量的堆栈内存,正如你在问题中所说的那样。
可以使用Stack Overflow替换迭代的实现,这有时是出于上述原因而完成的。
迭代也可以转换为尾递归的递归实现。
了解详情答案 3 :(得分:1)
大多数现有的答案参数通过引用递归的方式在C,C ++,Pascal,Java等过程语言中实现。 通过新的堆栈帧解决递归,并且在相同的堆栈帧内解决迭代。
我想补充一点除了技术实现差异之外,它们在概念上也是不同的。
所以,如果我将你的问题改为:在技术编译器特定的解决方案旁边,递归和迭代是否相同?
答案也是:不! 尝试解决着名的Fibonacci示例递归和迭代。
Fib(n) = Fib(n-1) + Fib(n-2)
Fib(1) = 1
Fib(0) = 0
递归方式:
Fib(5) = Fib(4) + Fib(3)
Fib(5) = Fib(3) + Fib(2) + Fib(2) + Fib(1)
Fib(5) = Fib(2) + Fib(1) + Fib(1) + Fib(0) + Fib(1) + Fib(0) + Fib(1)
Fib(5) = Fib(1) + Fib(0) + Fib(1) + Fib(1) + Fib(0) + Fib(1) + Fib(0) + Fib(1)
Fib(5) = 1 + 0 + 1 + 1 + 0 + 1 + 0 + 1
Fib(5) = 5
观察每行需要的空间越来越多!
加上操作:7
递归方法的运行时是O(n!),这太可怕了。
迭代方式:
Fib(0) = 0
Fib(1) = 1
Fib(2) = 0 + 1 = 1
Fib(3) = Fib(2) + Fib(1) = 2
Fib(4) = Fib(3) + Fib(2) = 3
Fib(5) = Fib(4) + Fib(3) = 5
观察每条线的长度是否相同!
加上操作:4
运行时为O(n)
所以:如果可能的话,总是更喜欢迭代解决方案而不是递归解决方案!
答案 4 :(得分:0)
递归涉及调用自身的函数/方法。迭代将成为单个函数体的一部分,因此它不会自称。