我一直在阅读几个人们选择使用Stack而不是递归的地方。这是因为递归被认为是完成工作的过时方法,或者这两种方法在不同的环境中是否同样适用?
答案 0 :(得分:15)
没有。恰好相反。递归是表达一类问题的自然方式。如果你没有递归,堆栈是一种模拟它的方法。
例如,请参阅此question。或者考虑一下标准的递归函数:计算 n -th Fibonacci数。
您会记得Fibonacci numbers是系列
0,1,1,2,3,5,8,13, ...
定义为F n = F n-1 + F n-2 。
这可以写为递归定义
基本情况:
F(0)= 0
F(1)= 1
递归步骤:
F(n)= F(n-1)+ F(n-2)
所以,你有F(0)= 0,F(1)= 1,F(2)= F(0)+ F(1)= 1,等等。
计算这个的简单程序(在C中只是为了咧嘴)是:
int fib(int n) {
/* we'll ignore possible negative arguments, see Wikipedia */
switch(n) {
case 0: return 0; break;
case 1: return 1; break;
default: return fib(n-1)+fib(n-2); break;
}
}
请注意该程序与原始定义的对应程度如何?
问题是,C管理调用堆栈中的所有中间结果。有些语言没有被定义为这样做(我唯一可以想到的是旧的FORTRAN,但我确信还有其他语言)。如果您使用汇编程序或旧FORTRAN编写,则必须管理自己的堆栈以跟踪这些中间结果。
答案 1 :(得分:10)
迭代通常比递归更快/更少开销。使用递归,我们隐式使用机器的堆栈作为我们的堆栈 - 我们“免费”获得 - 但我们支付昂贵的函数调用(以及随之而来的机器堆栈管理)的成本。
但递归函数通常更易于编写和阅读。
通常,可以使用递归编写函数,将其保留为瓶颈,然后将其替换为使用显式堆栈的迭代函数。
答案 2 :(得分:6)
更新了以包含鱼鳞修正。
使用堆栈是消除recursion
的标准技术Tail-Recursion的示例(可以使用迭代删除):
public class TailTest
{
public static void Main()
{
TailTest f = new TailTest();
f.DoTail(0);
}
public void DoTail(int n)
{
int v = n + 1;
System.Console.WriteLine(v);
DoTail(v); // Tail-Recursive call
}
}
答案 3 :(得分:4)
如果您处于编译语言/环境中,尾调用会增加堆栈(不应用尾调用优化(TCO)),那么最好避免深度递归,并且可能使用堆栈数据结构的迭代解决方案是首选
另一方面,如果您所在的语言/环境支持使用尾调用进行迭代,或者递归深度总是很小,那么递归通常是一个很好/优雅的解决方案。
(这有点过于宽泛,但总的来说,我绝不会将递归称为“过时”。)
答案 4 :(得分:3)
不,不,我认为现代开发人员应该在几毫秒内强调可读性和易维护性。
如果问题是递归的,我完全建议你的解决方案是递归的。
此外,您最终可能会引入一些不受欢迎的错误,以强制执行迭代/堆叠解决方案。