递归与手动堆栈 - 在哪种情况下首选哪个?

Posted

技术标签:

【中文标题】递归与手动堆栈 - 在哪种情况下首选哪个?【英文标题】:Recursion versus manual stacks - Which is preferred in which case? 【发布时间】:2012-02-24 18:36:28 【问题描述】:

递归程序会在内部创建堆栈,从而减少用户编写的代码。

除了上面提到的原因之外,是否存在递归实际上优于手动堆栈的情况?

编辑 1:

动态内存分配比递归程序在堆上的分配更“昂贵”吗?

【问题讨论】:

如果你的递归没有超过堆栈大小,那么你只使用调用堆栈而不是堆。 【参考方案1】:

当您说“更少的代码”时,我认为您暗指的主要原因是设计的清晰和简单。在具有局部变量和自动存储等功能的语言中,使用这些功能比将所有内容构建成家庭滚动堆栈要自然得多。 (毕竟,为什么要使用函数呢?为什么不使用if/elsewhile 作为你唯一的控制结构来编写你的整个程序呢?)

另一个考虑因素是性能,尤其是在多线程环境中。递归——取决于语言——可能会使用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);

以上是关于递归与手动堆栈 - 在哪种情况下首选哪个?的主要内容,如果未能解决你的问题,请参考以下文章

在哪种情况下首选 ICommand 和 Local:Mvx

我想反转堆栈,但我不知道如何使用递归来反转这个......如何在不使用递归的情况下反转堆栈

如果不允许 LP 递归,那么可能会出现堆栈溢出的情况?

python 3中使用堆栈的递归与迭代

我可以在没有递归和堆栈的情况下对二叉树进行顺序遍历吗?

在不使用堆栈/队列的情况下列出目录和子目录中的文件的非递归方式[关闭]