递归涉及使用隐式堆栈。这是由用于编译代码的编译器在后台实现的。
编译器创建的这个后台堆栈称为“调用堆栈”。调用堆栈可以使用堆栈数据结构来实现,堆栈数据结构存储有关计算机程序的活动子程序的信息。
每个子程序调用都使用调用堆栈内的一个框架,称为堆栈框架。当一个函数返回一个值时,它的栈帧会从调用栈中弹出。
递归的调用堆栈与显式调用堆栈?
堆栈溢出
这两个栈的根本区别在于编译器为程序的调用栈分配的空间是固定的。这意味着如果您不确定最大数量,将会发生堆栈溢出。预期的递归函数调用,并且调用太多,超出了分配给堆栈的空间在给定时间点可以处理的数量。
另一方面,如果你定义一个显式栈,它是在编译器在运行时分配给程序的堆空间上实现的。猜猜看,堆大小不是固定的,可以在运行时根据需要动态增加。您真的不必担心显式堆栈溢出。
空间和时间
对于给定的情况,哪一个会更快?
在不支持递归相关优化(例如尾递归的尾调用优化)的语言中,在显式堆栈上进行迭代可能比递归更快。
什么是尾递归?
尾递归是递归的一种特殊情况,递归函数在递归函数调用之后不再进行任何计算,即函数的最后一步是对递归函数的调用。
什么是尾调用优化 (TCO)?
尾调用优化是您能够避免为函数分配新堆栈帧的地方,因为调用函数将简单地返回它从被调用函数获得的值。
因此,支持尾调用优化的编译器/语言在调用堆栈中仅使用一个堆栈帧来实现对递归函数的调用。如果您的编译器/语言不支持这一点,那么使用显式堆栈将为您节省大量空间和时间。
Python 不支持尾调用优化。这样做的主要原因是有一个完整而清晰的堆栈跟踪,可以进行有效的调试。几乎所有的 C/C++ 编译器都支持尾调用优化。
有时显式控制堆栈有助于在使用多个参数时简化事情。
而递归解决方案使源代码的大小更小且更易于维护。
结论
最终,没有固定的答案。对于特定场景,需要考虑许多因素,例如可扩展性、代码可维护性、所使用的语言/编译器等。
最好的方法是使用两种方法来实施解决方案,在输入集上对 2 个解决方案进行计时,并在将其部署到生产设置之前分析峰值空间利用率。
见Wikipedia - Recursion versus Iteration