这个问题是通过研究C语言提出的。
我在我的数据结构课程中已经看到,在许多情况下,递归被证明是一种快速简单的解决方案(例如快速排序,遍历二叉搜索树等),已明确提到使用自创建的堆栈是一个更好的主意。
给出的理由是递归需要许多函数调用,函数调用速度较慢'。
但是,如果任何函数调用都使用堆栈,那么使用自创堆栈如何更好地证明?
答案 0 :(得分:2)
如果递归的深度很高,使用情况2意味着内存将很快耗尽。
答案 1 :(得分:2)
通常函数调用在函数内部完成之前会有一些开销。为函数调用生成的代码基本上可以确保当你返回时,你会发现像你一样的东西。同时它为您提供了被调用函数内的干净空环境。事实上,这种便利性是C提供的最重要的服务之一,紧挨着标准库。 (在许多其他方面,C只是一个宏汇编程序 - 您是否曾经并排查看C源代码和生成的汇编程序?)。
特别是通常必须保存一些寄存器,并且可能必须在调用堆栈上复制参数。所需的工作取决于处理器,编译器和调用约定。例如,参数和返回值可能在寄存器中,而不是在堆栈上(但是每次递归调用都必须保存参数,不是吗?)。
如果功能很小,开销相对较大;这就是为什么内联可以是强大的。内联递归函数调用类似于循环展开。我不知道当前的编译器是否会定期执行此操作(可能)。但是依赖编译器是有风险的,所以如果速度很重要的话,我会避免像计算阶乘一样的简单函数的递归实现。
答案 2 :(得分:2)
有两个真正的原因是自创堆栈比执行堆栈更有效:
执行堆栈旨在处理调用新函数的一般情况。这意味着它有很多开销:它必须包含指向前一个函数的指针,它必须包含指向堆上的值的指针,以及许多其他簿记项。如果您的计算确实具体,那么这可能比您的具体计算所需的更多。所有额外的管理都会降低效率。在功能非常繁重并且呼叫相对较少的情况下,这很好。在函数本身更简单但有许多函数调用的情况下,开销成本不成比例地增加。
广义堆栈隐藏了很多细节,阻止您利用直接引用堆栈的不同部分。例如,堆栈的根目录对您是隐藏的。假设您使用递归搜索大树中的特定值。在某些时候,您在树的深处有一千个节点并且您找到了该值。成功!但是你必须一次从树中爬出一个函数:意味着至少有一千个调用只是为了返回值。 (*)相反,如果您已经编写了自己的堆栈,则可以立即返回。或者,假设您有一个算法,在树中的某些节点上,要求您在继续执行之前备份n
堆栈帧。使用广义堆栈框架,您需要退出这些框架,直到找到您正在寻找的框架。如果您专门为算法设计了堆栈,则可以提供一种机制,以便在一条指令而不是n
中立即跳转到执行点。
因此,如果您可以利用丢弃不需要但不需要花费时间的广义堆栈框架机制的部分,或者编写的算法可以利用,那么您可以编写自己的堆栈如果它知道它在做什么,那么它就会快速地通过堆栈移动(广义堆栈通过隐藏它的抽象来防止你做这件事)。请记住,函数调用只是处理代码的一个特定的通用抽象:如果由于某种原因他们添加了一个使代码变得笨拙的约束,你可以创建一个更直接满足你需要的精简版本。
如果分配给堆栈的内存与您必须递归的次数相比较小,也可能会创建自己的堆栈,例如,如果您有一个非常大的输入域,或者如果您在专门的小型域上运行 - 脚印硬件或类似情况。但是,这又取决于您运行的算法以及通用堆栈解决方案如何帮助或阻碍它。
(*)尾递归通常会有所帮助,但由于尾递归只是定义只进入更深层次的堆栈帧,我假设您正在谈论不严格可能的情况。
答案 3 :(得分:0)
出于性能原因,可能不一定建议使用自己创建的堆栈。我能想到的一个很好的理由是“常规”堆栈可能具有固定大小(通常为1MB),因此例如对大量数据进行排序会导致堆栈溢出。