【发布时间】:2014-09-02 21:08:34
【问题描述】:
当被要求使用非递归算法来解决问题时,人们经常使用堆栈,但本质上堆栈和递归不一样吗?此外,当使用堆栈代替递归时,空间复杂度保持不变(渐近)。有什么我没能做到的根本区别吗 观察?
【问题讨论】:
当被要求使用非递归算法来解决问题时,人们经常使用堆栈,但本质上堆栈和递归不一样吗?此外,当使用堆栈代替递归时,空间复杂度保持不变(渐近)。有什么我没能做到的根本区别吗 观察?
【问题讨论】:
您的应用程序 stack 大小比数据结构 stack 更受限制。只要能动态分配内存(其实这次是依赖应用heap)就没有问题。
如上所述,您的应用程序堆栈受到更多限制,而且它具有每个临时局部变量、函数参数、返回值、堆栈指针和 ext 的副本。这使得它的尺寸比看起来更小。
【讨论】:
是的,完全一样。
使用堆栈而不是调用堆栈要么是一种变通方法(当一种语言的堆栈空间小于程序需要处理的最大值时),要么是一种可以节省一些空间的优化,因为堆栈帧通常需要一些语言中的机器词不会产生 TCO,或者您不需要以相同的方式存储数据,因此即使堆栈每个项目的空间效率较低,它也可以大大减少总消耗。
但并非所有语言都需要它,在语言 Racket 中,使用显式堆栈没有任何用途,因为堆栈没有任何限制,因为除了程序的总内存之外,即使使用非 TCO 调用也是如此(像树遍历)它仍然可以工作,直到所有内存都被消耗完。
在 Java 中,堆栈空间可在您启动程序时进行配置。它更喜欢阅读递归代码,因此在恢复使用显式堆栈之前检查您的语言是否可以做到这一点。
【讨论】:
Big-O 复杂度在某些情况下可能相同,但使用显式堆栈的常数因子通常更好。此外,机器执行堆栈的大小通常相对较小,而显式(堆分配)堆栈可以变得更大。
有时您会想要寻找一种完全不同的算法,该算法恰好是非递归的,并且性能会更好。考虑朴素的斐波那契序列算法:
int f(int n)
{
if (n < 2)
{
return 1;
}
return f(n-2) + f(n-1);
}
这个算法需要指数级的时间:(
非递归版本(在这种情况下实际上是“动态编程”的一种形式)仅在n 中是线性的,并且不使用堆栈:
int f(int n)
{
int fMinusOne = 1;
int fMinusTwo = 1;
for (int idx = 1; idx < n; ++idx)
{
int next = fMinusOne + fMinusTwo;
fMinusTwo = fMinusOne;
fMinusOne = next;
}
return fMinusOne;
}
【讨论】: