Recursion 显式堆栈比递归好吗

Recursion 显式堆栈比递归好吗,recursion,linked-list,stack,Recursion,Linked List,Stack,我们可以使用堆栈和递归以相反顺序打印链表。我的老师说使用显式堆栈更好,因为递归也使用堆栈,但必须维护许多其他参数。即使我们使用stack中的std::stack,引用外部库是否也会占用时间?与使用递归解决方案相比,使用显式堆栈如何节省时间/空间?递归涉及使用隐式堆栈。这是由用于编译代码的编译器在后台实现的。 这个由编译器创建的后台堆栈称为“调用堆栈”。调用堆栈可以使用堆栈数据结构来实现,堆栈数据结构存储有关计算机程序的活动子例程的信息 每个子例程调用都使用调用堆栈中称为堆栈帧的帧。当函数返回一个

我们可以使用堆栈和递归以相反顺序打印链表。我的老师说使用显式堆栈更好,因为递归也使用堆栈,但必须维护许多其他参数。即使我们使用
stack
中的
std::stack
,引用外部库是否也会占用时间?与使用递归解决方案相比,使用显式堆栈如何节省时间/空间?

递归涉及使用隐式堆栈。这是由用于编译代码的编译器在后台实现的。 这个由编译器创建的后台堆栈称为“调用堆栈”。调用堆栈可以使用堆栈数据结构来实现,堆栈数据结构存储有关计算机程序的活动子例程的信息

每个子例程调用都使用调用堆栈中称为堆栈帧的帧。当函数返回一个值时,它的堆栈帧从调用堆栈弹出

递归的调用堆栈与显式调用堆栈? 堆栈溢出 这两个堆栈之间的根本区别在于编译器为程序的调用堆栈分配的空间是固定的。这意味着如果您不确定预期的递归函数调用的最大数量,并且在给定时间点分配给堆栈的空间无法处理的调用太多,那么将出现堆栈溢出。 另一方面,如果定义一个显式堆栈,它将在编译器在运行时分配给程序的堆空间上实现。猜猜看,堆大小不是固定的,可以在需要时在运行时动态增加。您实际上不必担心显式堆栈溢出

时空 在特定情况下,哪一个会更快? 在不支持递归相关优化(如尾部递归的尾部调用优化)的语言中,在显式堆栈上迭代可能比递归更快

什么是尾部递归? 尾部递归是递归的一种特殊情况,递归函数在调用递归函数后不再进行任何计算,即函数的最后一步是调用递归函数

什么是尾部呼叫优化(TCO)? 尾部调用优化可以避免为函数分配新的堆栈帧,因为调用函数只返回从被调用函数获得的值

因此,支持尾部调用优化的编译器/语言只在调用堆栈中使用一个堆栈帧来实现对递归函数的调用。如果您的编译器/语言不支持此功能,那么使用显式堆栈将节省大量空间和时间

Python不支持尾部调用优化。这样做的主要原因是要有一个完整而清晰的堆栈跟踪,从而实现高效的调试。几乎所有C/C++编译器都支持尾部调用优化

有时,当使用多个参数时,显式控制堆栈有助于简化工作。 然而,递归解决方案使源代码的大小更小,更易于维护

结论 最后,没有固定的答案。对于特定场景,需要考虑许多因素,例如可伸缩性、代码可维护性、使用的语言/编译器等。 最好的方法是使用这两种方法来实现解决方案,在一个输入集上计时两个解决方案,并在将其部署到生产设置之前分析峰值空间利用率


参见

递归涉及隐式堆栈的使用。这是由用于编译代码的编译器在后台实现的。 这个由编译器创建的后台堆栈称为“调用堆栈”。调用堆栈可以使用堆栈数据结构来实现,堆栈数据结构存储有关计算机程序的活动子例程的信息

每个子例程调用都使用调用堆栈中称为堆栈帧的帧。当函数返回一个值时,它的堆栈帧从调用堆栈弹出

递归的调用堆栈与显式调用堆栈? 堆栈溢出 这两个堆栈之间的根本区别在于编译器为程序的调用堆栈分配的空间是固定的。这意味着如果您不确定预期的递归函数调用的最大数量,并且在给定时间点分配给堆栈的空间无法处理的调用太多,那么将出现堆栈溢出。 另一方面,如果定义一个显式堆栈,它将在编译器在运行时分配给程序的堆空间上实现。猜猜看,堆大小不是固定的,可以在需要时在运行时动态增加。您实际上不必担心显式堆栈溢出

时空 在特定情况下,哪一个会更快? 在不支持递归相关优化(如尾部递归的尾部调用优化)的语言中,在显式堆栈上迭代可能比递归更快

什么是尾部递归? 尾部递归是递归的一种特殊情况,递归函数在调用递归函数后不再进行任何计算,即函数的最后一步是调用递归函数

什么是尾部呼叫优化(TCO)? 尾部调用优化可以避免为函数分配新的堆栈帧,因为调用函数只返回从被调用函数获得的值

因此,支持尾部调用优化的编译器/语言只在调用堆栈中使用一个堆栈帧来实现对递归函数的调用。如果您的编译器/语言不支持此功能,那么使用显式堆栈将节省大量空间和时间

Python不支持尾部调用优化。这样做的主要原因是要有一个完整而清晰的堆栈跟踪