遍历BST时出现Stackoverflow异常
我已经在 C++ 中为我的一项作业实现了基于链接的 BST(二叉搜索树)。我已经编写了整个课程,一切都运行良好,但我的作业要求我绘制运行时间:
a. A sorted list of 50000, 75000, and 100000 items
b. A random list of 50000, 75000, and 100000 items
没关系,我可以插入数字,但它还要求我调用 FindHeight() 和树上的 CountLeaves()
方法。我的问题是我使用递归
实现了这两个函数。由于我有一个如此大的数字列表,我收到了 stackoverflow 异常。
这是我的类定义:
template <class TItem>
class BinarySearchTree
{
public:
struct BinarySearchTreeNode
{
public:
TItem Data;
BinarySearchTreeNode* LeftChild;
BinarySearchTreeNode* RightChild;
};
BinarySearchTreeNode* RootNode;
BinarySearchTree();
~BinarySearchTree();
void InsertItem(TItem);
void PrintTree();
void PrintTree(BinarySearchTreeNode*);
void DeleteTree();
void DeleteTree(BinarySearchTreeNode*&);
int CountLeaves();
int CountLeaves(BinarySearchTreeNode*);
int FindHeight();
int FindHeight(BinarySearchTreeNode*);
int SingleParents();
int SingleParents(BinarySearchTreeNode*);
TItem FindMin();
TItem FindMin(BinarySearchTreeNode*);
TItem FindMax();
TItem FindMax(BinarySearchTreeNode*);
};
FindHeight() 实现
template <class TItem>
int BinarySearchTree<TItem>::FindHeight()
{
return FindHeight(RootNode);
}
template <class TItem>
int BinarySearchTree<TItem>::FindHeight(BinarySearchTreeNode* Node)
{
if(Node == NULL)
return 0;
return 1 + max(FindHeight(Node->LeftChild), FindHeight(Node->RightChild));
}
CountLeaves() 实现
template <class TItem>
int BinarySearchTree<TItem>::CountLeaves()
{
return CountLeaves(RootNode);
}
template <class TItem>
int BinarySearchTree<TItem>::CountLeaves(BinarySearchTreeNode* Node)
{
if(Node == NULL)
return 0;
else if(Node->LeftChild == NULL && Node->RightChild == NULL)
return 1;
else
return CountLeaves(Node->LeftChild) + CountLeaves(Node->RightChild);
}
我试图思考如何在不使用递归的情况下实现这两个方法,但我完全被难住了。有人有什么想法吗?
I have implement a link-based BST (binary search tree) in C++ for one of my assignment. I have written my whole class and everything works good, but my assignment asks me to plot the run-times for:
a. A sorted list of 50000, 75000, and 100000 items
b. A random list of 50000, 75000, and 100000 items
That's fine, I can insert the numbers but it also asks me to call the FindHeight()
and CountLeaves()
methods on the tree. My problem is that I've implemented the two functions using recursion
. Since I have a such a big list of numbers I'm getting getting a stackoverflow
exception.
Here's my class definition:
template <class TItem>
class BinarySearchTree
{
public:
struct BinarySearchTreeNode
{
public:
TItem Data;
BinarySearchTreeNode* LeftChild;
BinarySearchTreeNode* RightChild;
};
BinarySearchTreeNode* RootNode;
BinarySearchTree();
~BinarySearchTree();
void InsertItem(TItem);
void PrintTree();
void PrintTree(BinarySearchTreeNode*);
void DeleteTree();
void DeleteTree(BinarySearchTreeNode*&);
int CountLeaves();
int CountLeaves(BinarySearchTreeNode*);
int FindHeight();
int FindHeight(BinarySearchTreeNode*);
int SingleParents();
int SingleParents(BinarySearchTreeNode*);
TItem FindMin();
TItem FindMin(BinarySearchTreeNode*);
TItem FindMax();
TItem FindMax(BinarySearchTreeNode*);
};
FindHeight() Implementation
template <class TItem>
int BinarySearchTree<TItem>::FindHeight()
{
return FindHeight(RootNode);
}
template <class TItem>
int BinarySearchTree<TItem>::FindHeight(BinarySearchTreeNode* Node)
{
if(Node == NULL)
return 0;
return 1 + max(FindHeight(Node->LeftChild), FindHeight(Node->RightChild));
}
CountLeaves() implementation
template <class TItem>
int BinarySearchTree<TItem>::CountLeaves()
{
return CountLeaves(RootNode);
}
template <class TItem>
int BinarySearchTree<TItem>::CountLeaves(BinarySearchTreeNode* Node)
{
if(Node == NULL)
return 0;
else if(Node->LeftChild == NULL && Node->RightChild == NULL)
return 1;
else
return CountLeaves(Node->LeftChild) + CountLeaves(Node->RightChild);
}
I tried to think of how I can implement the two methods without recursion but I'm completely stumped. Anyone have any ideas?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
如果平衡的话,在具有 100,000 个节点的树上递归应该不是问题。深度可能只有 17,在所示的实现中不会使用太多堆栈。
(log2(100,000) = 16.61)
。所以看来构建树的代码可能没有正确平衡它。Recursion on a tree with 100,000 nodes should not be a problem if it is balanced. The depth would only be maybe 17, which would not use very much stack in the implementations shown.
(log2(100,000) = 16.61)
. So it seems that maybe the code that is building the tree is not balancing it correctly.我发现此页面非常有启发性,因为它讨论了将使用递归的函数转换为递归的机制使用迭代。
它也有显示代码的示例。
I found this page very enlightening because it talks about the mechanics of converting a function that uses recursion to one that uses iteration.
It has examples showing code as well.
可能您需要在插入时计算这个。存储节点的高度,即在Node对象中添加一个整数字段,如高度。还有树的柜台高度和叶子。当您插入一个节点时,如果它的父节点是(曾经)叶子,则叶子计数不会改变,但如果不是,则将叶子计数增加 1。此外,新节点的高度是父节点的高度 + 1,因此如果它更大比树的当前高度高,然后更新它。这是一项作业,所以我不会帮助实际代码
May be you need to calculate this while doing the insert. Store the heights of nodes, i.e add an integer field like height in the Node object. Also have counters height and leaves for the tree. When you insert a node, if its parent is (was) a leaf, the leaf count doesnt change, but if not, increase leaf count by 1. Also the height of the new node is parent's height + 1, hence if that is greater than the current height of the tree, then update it. Its a homework, so i wont help with the actual code
偶尔平衡你的树。如果您的树在 FindHeight() 上出现堆栈溢出,则意味着您的树方式不平衡。如果树是平衡的,对于 100000 个元素,它的深度应该仅为 20 个左右的节点。
重新平衡不平衡二叉树的最简单(但相当慢)的方法是分配一个足够大的
TItem
数组来容纳树中的所有数据,将所有数据按排序插入其中排序,并删除所有节点。然后递归地从数组重建树。根是中间的节点。root->left
是左半部的中间,root->right
是右半部的中间。递归地重复。这是最简单的重新平衡方法,但速度较慢并且会暂时占用大量内存。另一方面,只有当您检测到树非常不平衡(插入深度超过 100)时才需要执行此操作。另一个(更好的)选项是在插入期间保持平衡。最直观的方法是跟踪当前节点下方有多少个节点。如果右子节点的“子”节点数量是左子节点的两倍以上,则向左“旋转”。反之亦然。互联网上到处都有关于如何进行树旋转的说明。这使得插入速度稍微慢一些,但是这样就不会出现第一个选项偶尔造成的大规模停顿。另一方面,您必须在进行轮换时不断更新所有“子项”计数,这并非易事。
Balance your tree occasionally. If your tree is getting stackoverflow on FindHeight(), that means your tree is way unbalanced. If the tree is balanced it should only have a depth of about 20 nodes for 100000 elements.
The easiest (but fairly slow) way of re-balancing unbalanced binary tree is to allocate an array of
TItem
big enough to hold all of the data in the tree, insert all of your data into it in sorted order, and delete all of the nodes. Then rebuild the tree from the array recursively. The root is the node in the middle.root->left
is the middle of the left half,root->right
is the middle of the right half. Repeat recursively. This is the easiest way to rebalance, but it is slowish and takes lots of memory temporarily. On the other hand, you only have to do this when you detect that the tree is very unbalanced, (depth on insert is more than 100).The other (better) option is to balance during inserts. The most intuitive way to do this is to keep track of how many nodes are beneath the current node. If the right child has more than twice as many "child" nodes as the left child, "rotate" left. And vice-versa. There's instrcutions on how to do tree rotates all over the internet. This makes inserts slightly slower, but then you don't have occassional massive stalls that the first option creates. On the other hand, you have to constantly update all of the "children" counts as you do the rotates, which isn't trivial.
为了在不递归的情况下对叶子进行计数,请使用迭代器的概念,就像 STL 用于底层
std::set
和std::map
的 RB 树一样。为您的树创建一个begin()
和end()
函数,用于标识有序的第一个和最后一个节点(在本例中是最左边的节点,然后是最右边的节点)大多数节点)。然后创建一个名为BinarySearchTreeNode*increment(const BinarySearchTreeNode* current_node)
的函数,对于给定的
current_node
,它将返回指向树中下一个节点的指针。请记住,要使此实现正常工作,您的node
类型中需要一个额外的parent
指针来帮助迭代过程。increment()
的算法如下所示:最后,您需要一个 bool leaf(const BinarySearchTreeNode* current_node) 函数来测试给定节点是否是叶节点。因此,计数器函数可以简单地迭代树并找到所有叶节点,完成后返回最终计数。
如果您想在不使用递归的情况下测量不平衡树的最大深度,则需要在树的
insert()
函数中跟踪节点插入的深度。这可以只是node
类型中的一个变量,在将节点插入树中时设置。然后,您可以迭代这三个节点,并找到叶节点的最大深度。顺便说一句,不幸的是,该方法的复杂度将是 O(N) ...远不如 O(log N) 那么好。
In order to count the leaves without recursion, use the concept of an iterator like the STL uses for the RB-tree underlying
std::set
andstd::map
... Create abegin()
andend()
function for you tree that indentifies the ordered first and last node (in this case the left-most node and then the right-most node). Then create a function calledBinarySearchTreeNode* increment(const BinarySearchTreeNode* current_node)
that for a given
current_node
, will return a pointer to the next node in the tree. Keep in mind for this implementation to work, you will need an extraparent
pointer in yournode
type to aid in the iteration process.Your algorithm for
increment()
would look something like the following:Finally you'll need a
bool leaf(const BinarySearchTreeNode* current_node)
function that will test whether a given node is a leaf node. Thus you counter function can simply iterate though the tree and find all the leaf nodes, returning a final count once it's done.If you want to measure the maximum depth of an unbalanced tree without recursion, you will, in your tree's
insert()
function, need to keep track of the depth that a node was inserted at. This can simply be a variable in yournode
type that is set when the node is inserted in the tree. You can then iterate through the three, and find the maximum depth of a leaf-node.BTW, the complexity of this method is unfortunately going to be O(N) ... nowhere near as nice as O(log N).