【问题标题】:Isn't using a stack same as recursion?使用堆栈与递归不一样吗?
【发布时间】:2014-09-02 21:08:34
【问题描述】:

当被要求使用非递归算法来解决问题时,人们经常使用堆栈,但本质上堆栈和递归不一样吗?此外,当使用堆栈代替递归时,空间复杂度保持不变(渐近)。有什么我没能做到的根本区别吗 观察?

【问题讨论】:

标签: recursion stack


【解决方案1】:

您的应用程序 stack 大小比数据结构 stack 更受限制。只要能动态分配内存(其实这次是依赖应用heap)就没有问题。

如上所述,您的应用程序堆栈受到更多限制,而且它具有每个临时局部变量、函数参数、返回值、堆栈指针和 ext 的副本。这使得它的尺寸比看起来更小。

【讨论】:

  • 这取决于。检查应用程序堆栈通常比检查堆分配的堆栈更容易。转储跟踪时,几乎每个调试器都可以看到机器堆栈。指向堆分配堆栈的指针可能已损坏。
  • 其实你是对的。借助现代编辑器和调试器,您的案例更具有说服力。
  • 但应用程序堆栈会因为局部性而允许更快的访问。
  • @RAY66 如果 stack 数据结构是基于动态内存分配和指针重定向的,这是正确的,这是大多数情况。您仍然可以通过预先分配预期数量的所需对象空间来提高性能,这将增加堆栈中引用的局部性。
  • @RAY:这里的本地性应该因为必须维护机器堆栈而相形见绌。 (优化器在理解循环方面比理解递归要好得多)但是对于所有性能问题,在使用分析器进行测量之前您才真正知道。
【解决方案2】:

是的,完全一样。

使用堆栈而不是调用堆栈要么是一种变通方法(当一种语言的堆栈空间小于程序需要处理的最大值时),要么是一种可以节省一些空间的优化,因为堆栈帧通常需要一些语言中的机器词不会产生 TCO,或者您不需要以相同的方式存储数据,因此即使堆栈每个项目的空间效率较低,它也可以大大减少总消耗。

但并非所有语言都需要它,在语言 Racket 中,使用显式堆栈没有任何用途,因为堆栈没有任何限制,因为除了程序的总内存之外,即使使用非 TCO 调用也是如此(像树遍历)它仍然可以工作,直到所有内存都被消耗完。

在 Java 中,堆栈空间可在您启动程序时进行配置。它更喜欢阅读递归代码,因此在恢复使用显式堆栈之前检查您的语言是否可以做到这一点。

【讨论】:

  • 谢谢!这就是我想知道的。
【解决方案3】:

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;
}

【讨论】:

  • 我明白你的意思,但我的问题是从概念的角度来看,它们不是一个吗?
  • @RAY:这取决于算法。如果您使用显式堆栈模拟递归,当然可以。您正在模拟递归,因此希望您的模拟操作类似。但大多数时候,当有人要求“非递归解决方案”时,他们想要一个完全不同的算法。
  • 让我重新表述这个问题“堆栈和递归是否相同?”
  • @RAY:我相信我回答了你的问题。
  • 有什么方法可以在不隐式或显式使用堆栈 ADT 的情况下模拟递归?
猜你喜欢
  • 1970-01-01
  • 2014-12-10
  • 1970-01-01
  • 2021-11-17
  • 1970-01-01
  • 2011-11-08
  • 2018-10-16
  • 1970-01-01
  • 2013-06-16
相关资源
最近更新 更多