遍历BST时出现Stackoverflow异常
Stackoverflow exception when traversing BST
我在c++中实现了一个基于链接的BST(二叉搜索树)。我已经写了整个类,一切都很好,但我的作业要求我绘制运行时间:
a. A sorted list of 50000, 75000, and 100000 items
b. A random list of 50000, 75000, and 100000 items
很好,我可以插入数字,但它也要求我调用树上的FindHeight()
和CountLeaves()
方法。我的问题是,我已经实现了两个功能使用recursion
。因为我有一个这么大的数字列表,我正在得到一个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);
}
我试着思考如何在没有递归的情况下实现这两个方法,但我完全被难住了。有人有什么想法吗?
如果是平衡的,100,000个节点的树上的递归应该不是问题。深度可能只有17,在所示的实现中不会使用太多堆栈。(log2(100,000) = 16.61)
。因此,似乎是构建树的代码没有正确地平衡它。
我发现这一页非常有启发性,因为它讨论了将使用递归的函数转换为使用迭代的函数的机制。
它也有显示代码的示例。
可能您需要在执行插入时计算此值。存储节点的高度,即在Node对象中添加一个像高度这样的整数字段。也有计数器高度和树叶的树。当你插入一个节点时,如果它的父节点是(曾经是)一个叶节点,那么叶节点计数不会改变,但如果不是,则增加叶节点计数1。此外,新节点的高度是父节点的高度+ 1,因此,如果它大于树的当前高度,则更新它。这是一个家庭作业,所以我不会帮助实际的代码
平衡你的树偶尔。如果你的树在FindHeight()上得到stackoverflow,这意味着你的树是方式不平衡。如果树是平衡的,那么100000个元素的深度应该只有20个节点。
重新平衡非平衡二叉树的最简单(但相当缓慢)的方法是分配一个足够大的TItem
数组来容纳树中的所有数据,按排序顺序插入所有数据,并删除所有节点。然后从数组递归地重新构建树。根节点是中间的节点。root->left
是左半部分的中间,root->right
是右半部分的中间。重复递归。这是最简单的重新平衡方法,但它很慢,并且暂时占用大量内存。另一方面,只有当您检测到树非常不平衡(插入深度超过100)时才需要执行此操作。
另一个(更好的)选项是在插入期间进行平衡。最直观的方法是跟踪当前节点下面有多少个节点。如果右子节点的"子"节点数是左子节点的两倍以上,则向左"旋转"。反之亦然。网上到处都是如何旋转树的说明。这使得插入稍微慢一些,但这样就不会出现第一个选项造成的偶尔的大量停顿。另一方面,您必须在旋转时不断更新所有"子"计数,这不是微不足道的。
为了在没有递归的情况下计算叶子,可以使用迭代器的概念,就像STL对std::set
和std::map
底层的rb树所使用的那样…为您的树创建一个begin()
和end()
函数,用于标识有序的第一个和最后一个节点(在本例中是最左边的节点,然后是最右边的节点)。然后创建一个名为
BinarySearchTreeNode* increment(const BinarySearchTreeNode* current_node)
对于给定的current_node
,将返回指向树中下一个节点的指针。请记住,为了使此实现工作,您需要在node
类型中添加一个额外的parent
指针,以帮助迭代过程。
你的increment()
算法看起来像这样:
- 检查当前节点是否有右子节点。
- 如果有右子树,使用while循环查找右子树的最左边的节点。这将是"下一个"节点。
- 如果当前节点没有右子节点,则检查当前节点是否是其父节点的左子节点。
- 如果步骤#3为真,则"下一个"节点为父节点,因此您可以在此停止,否则执行下一步。
- 如果步骤#3为假,则当前节点是父节点的右子节点。因此,您需要使用while循环继续向上移动到下一个父节点,直到遇到其父节点的左子节点为止。这个左子节点的父节点将是"下一个"节点,你可以停止。
- 最后,如果步骤#5返回到根节点,那么当前节点就是树中的最后一个节点,迭代器已经到达树的末端。
最后,您将需要一个bool leaf(const BinarySearchTreeNode* current_node)
函数来测试给定节点是否为叶节点。因此,计数器函数可以简单地遍历树并找到所有叶节点,完成后返回最终计数。
如果你想在没有递归的情况下测量不平衡树的最大深度,你将需要在树的insert()
函数中跟踪节点插入的深度。这可以是node
类型中的一个变量,当节点插入到树中时设置它。然后,您可以遍历这三个节点,并找到叶节点的最大深度。
BTW,不幸的是,这种方法的复杂性将是O(N)…远不如O(log N)好
- 处理多个异常集合的C++方法
- 我在c++代码中生成了一个运行时#3异常
- 孤立代码块在结构中引发异常
- C++中的赋值发生,尽管右侧出现异常
- 从构造函数抛出异常时如何克服内存泄漏
- 异常属于C++中的线程还是进程
- 当类定义不可见时捕获异常
- 引发异常:读取访问冲突**dynamicArray**为0x1118235.发生
- 为什么异常不退出程序?
- 为什么我应该在异常处理中使用std::cerr而不是std::cout
- 如何修复链表类实现的未处理异常0xDDDDDDDD
- 关于:C++中异常对象的范围:为什么我没有得到副本?
- 是什么导致了Unity 3D中的"错误线程异常"?
- 如何将strftime中的格式错误作为异常捕获
- 创建具有 new in 函数和"this is nullptr"异常的对象
- 尝试使用智能指针时引发异常
- 从CLR GUI实现非托管班级时,stackoverflow异常提出
- 从 WebAPI 项目运行静态类时出现 StackOverflow 异常 - 从控制台应用程序运行时工作正常
- 遍历BST时出现Stackoverflow异常
- System类型的未处理异常.my.dll中出现StackOverflow异常