堆栈是如何组织的? : 删除二叉树
How is the stack organized? : Deleting a Binary Tree
我有这段代码可以从内存中删除二叉树,但我不知道当您对 destroy(<#node* tree#>)
进行递归调用时堆栈的外观或递归的工作方式。我知道当你到达分支的末尾时递归结束,因此得出调用并开始在递归中上升的结论,但是如果递归函数调用保持在它停止的地方,那么对 destroy(tree->right)
的调用是否等待在 destroy(tree->left)
完成时执行?
struct node{
int value;
node* left;
node* right;
};
void destroy(node* tree){
if(tree != NULL){
destroy(tree->left);
destroy(tree->right);
delete tree;
}
}
你必须拿一张纸来形象化它,但是,是的,它们是连续的,树上的两个地方不要有相同的节点,否则你会遇到问题。
它会像
destroy(root)
>destroy(l)
>l
>l
(...)
>r
(...)
>r
>l
(...)
>r
(...)
>delete root
>end
因此,您将开始一个接一个地推送地址,直到您最终到达分支的末尾,然后弹出一次,并继续探索直到下一个分支的末尾,依此类推。最后,在最后一个分支上,弹出所有函数,直到回到原来的函数,然后删除根节点。
destroy(tree->left) 在 destroy(tree->right) 之前完全执行。
但是,请记住左树有左边和右边。所以在这里它会再次先向左走,然后在返回的路上向右走。右侧可能包含最先处理的左侧节点。
堆栈对此影响不大。每当您深入树中的一个杠杆(即在左侧或右侧调用 destroy)时,函数调用可能会将一些变量压入堆栈 - 例如 return 地址和当前节点指针。当到达一片叶子并且函数调用开始 returning 时,这些变量将再次从堆栈中取出。
通常这应该不是课程问题,但如果您的树有很多级别,您可能需要考虑堆栈的使用。
如果您假设一个空堆栈开始并查看第一个 destroy left 调用,您将有:
堆栈 = |左一|
现在这可能会导致另一个调用 left:
stack = |第一个左边|左二|
甚至第三次向左呼叫:
stack = |第一个左边|左二|左三|
现在呼叫左三 returns:
stack = |第一个左边|左二|
但是右边会叫:
stack = |第一个左边|左二|第一个右边 |
这个右边可能有一个节点在左边所以我们会得到:
stack = |第一个左边|左二|第一个权利 |第一个离开 |
这将持续到所有节点 returns - 就像这样
stack = |第一个左边|左二|第一个右边 |
stack = |第一个左边|左二|
堆栈 = |左一|
堆栈=
现在是时候从右侧的顶部做同样的事情了
堆栈 = |第一个右边|
等等...
对于代码中的简单示例,它很可能会导致树中每个级别的 4 字节 return 地址被推送到堆栈。因此,即使树级别相当深,堆栈使用率也会很低。
我有这段代码可以从内存中删除二叉树,但我不知道当您对 destroy(<#node* tree#>)
进行递归调用时堆栈的外观或递归的工作方式。我知道当你到达分支的末尾时递归结束,因此得出调用并开始在递归中上升的结论,但是如果递归函数调用保持在它停止的地方,那么对 destroy(tree->right)
的调用是否等待在 destroy(tree->left)
完成时执行?
struct node{
int value;
node* left;
node* right;
};
void destroy(node* tree){
if(tree != NULL){
destroy(tree->left);
destroy(tree->right);
delete tree;
}
}
你必须拿一张纸来形象化它,但是,是的,它们是连续的,树上的两个地方不要有相同的节点,否则你会遇到问题。
它会像
destroy(root)
>destroy(l)
>l
>l
(...)
>r
(...)
>r
>l
(...)
>r
(...)
>delete root
>end
因此,您将开始一个接一个地推送地址,直到您最终到达分支的末尾,然后弹出一次,并继续探索直到下一个分支的末尾,依此类推。最后,在最后一个分支上,弹出所有函数,直到回到原来的函数,然后删除根节点。
destroy(tree->left) 在 destroy(tree->right) 之前完全执行。
但是,请记住左树有左边和右边。所以在这里它会再次先向左走,然后在返回的路上向右走。右侧可能包含最先处理的左侧节点。
堆栈对此影响不大。每当您深入树中的一个杠杆(即在左侧或右侧调用 destroy)时,函数调用可能会将一些变量压入堆栈 - 例如 return 地址和当前节点指针。当到达一片叶子并且函数调用开始 returning 时,这些变量将再次从堆栈中取出。
通常这应该不是课程问题,但如果您的树有很多级别,您可能需要考虑堆栈的使用。
如果您假设一个空堆栈开始并查看第一个 destroy left 调用,您将有:
堆栈 = |左一|
现在这可能会导致另一个调用 left:
stack = |第一个左边|左二|
甚至第三次向左呼叫:
stack = |第一个左边|左二|左三|
现在呼叫左三 returns:
stack = |第一个左边|左二|
但是右边会叫:
stack = |第一个左边|左二|第一个右边 |
这个右边可能有一个节点在左边所以我们会得到:
stack = |第一个左边|左二|第一个权利 |第一个离开 |
这将持续到所有节点 returns - 就像这样
stack = |第一个左边|左二|第一个右边 |
stack = |第一个左边|左二|
堆栈 = |左一|
堆栈=
现在是时候从右侧的顶部做同样的事情了
堆栈 = |第一个右边|
等等...
对于代码中的简单示例,它很可能会导致树中每个级别的 4 字节 return 地址被推送到堆栈。因此,即使树级别相当深,堆栈使用率也会很低。