递归与手动堆栈 - 在哪种情况下首选哪个?
Posted
技术标签:
【中文标题】递归与手动堆栈 - 在哪种情况下首选哪个?【英文标题】:Recursion versus manual stacks - Which is preferred in which case? 【发布时间】:2012-02-24 18:36:28 【问题描述】:递归程序会在内部创建堆栈,从而减少用户编写的代码。
除了上面提到的原因之外,是否存在递归实际上优于手动堆栈的情况?
编辑 1:
动态内存分配比递归程序在堆上的分配更“昂贵”吗?
【问题讨论】:
如果你的递归没有超过堆栈大小,那么你只使用调用堆栈而不是堆。 【参考方案1】:当您说“更少的代码”时,我认为您暗指的主要原因是设计的清晰和简单。在具有局部变量和自动存储等功能的语言中,使用这些功能比将所有内容构建成家庭滚动堆栈要自然得多。 (毕竟,为什么要使用函数呢?为什么不使用if
/else
和while
作为你唯一的控制结构来编写你的整个程序呢?)
另一个考虑因素是性能,尤其是在多线程环境中。递归——取决于语言——可能会使用the stack(注意:你说“在内部创建一个堆栈”,但实际上,它使用的是此类语言中的程序总是拥有的堆栈),而手动堆栈结构需要dynamic memory allocation,这通常会带来明显的性能损失——更不用说确保在(比如说)遇到异常时释放所有内存所增加的复杂性。
【讨论】:
您为使用系统堆栈的性能所做的权衡是,与使用堆上的堆栈数据结构相比,使用系统堆栈的递归深度通常受到更多限制,因为堆要大得多。whereas a manual stack structure would require dynamic memory allocation, which frequently has a noticeable performance penalty
这很重要。但是重复的函数调用不是性能损失吗?不是比手动分配内存大吗?
@SethCarnegie:是的,绝对是,好点。在许多平台上,堆内存用完比堆栈溢出更容易处理。由于这个问题是关于使用递归的原因,我没有提到这些事情,但也许我应该有,无论如何,只是为了完整性?
@AnishaKaul:与往常一样,如果性能如此重要,那么您需要在您关心的平台上对其进行测试;但一般来说 - 如果您使用链表作为堆栈,那么我希望动态内存分配比重复的函数调用更昂贵,但是如果您使用动态可调整大小的数组并从末尾添加/删除元素,那么您很有可能将动态内存分配的数量减少到相当便宜的程度。然而,问题在于,如果你的目标是消除重复的函数调用,那么,你打算做什么
动态内存分配在什么情况下会“昂贵”?不只是分配和释放内存吗?【参考方案2】:
我基本同意@ruakh 的回答。我只想补充一点,使用系统堆栈有很多开销(实际上,每次递归时,你推送的状态比你需要的多得多),并且可能会导致非常深(但有界)递归的堆栈溢出,你可能能够通过使用显式堆栈并仅推送您需要的状态来避免。
【讨论】:
Mac,请看我对 ruakh 回答的评论。【参考方案3】:在外部使用堆栈
vector<int> Solution::inorderTraversal(TreeNode* A)
vector<int> res;
stack<TreeNode* > st;
TreeNode* root=A;
while(true)
if(root==NULL)
if(st.empty())
break;
root=st.top();
st.pop();
res.push_back(root->val);
root=root->right;
else
st.push(root);
root=root->left;
return res;
使用递归
void inorder(struct node* root)
但在这里我们看到,在外部使用堆栈可以节省大量处理时间,因此外部堆栈方法更快。
【讨论】:
void inorder(struct node* root) if(root!=NULL) inorder(root->left);coutval;inorder(root->right);以上是关于递归与手动堆栈 - 在哪种情况下首选哪个?的主要内容,如果未能解决你的问题,请参考以下文章