我在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);
}
我试着想一想如何在没有递归的情况下实现这两种方法,但我完全被难倒了。有人有什么想法吗?
答案 0 :(得分:3)
如果平衡,则具有100,000个节点的树上的递归应该不是问题。深度可能只有17,在所示的实现中不会使用非常多的堆栈。 (log2(100,000) = 16.61)
。所以似乎构建树的代码可能没有正确平衡它。
答案 1 :(得分:2)
我发现this page非常具有启发性,因为它讨论了将使用递归的函数转换为使用迭代的函数的机制。
它还有显示代码的示例。
答案 2 :(得分:1)
为了计算没有递归的叶子,使用迭代器的概念,如STL用于基础std::set
和std::map
的RB树...创建begin()
和end()
函数为您的树识别有序的第一个和最后一个节点(在这种情况下是最左边的节点,然后是最右边的节点)。然后创建一个名为
BinarySearchTreeNode* increment(const BinarySearchTreeNode* current_node)
对于给定的current_node
,将返回指向树中下一个节点的指针。请记住,要使此实现正常工作,您需要在parent
类型中添加额外的node
指针以辅助迭代过程。
increment()
的算法类似于以下内容:
最后,您需要一个bool leaf(const BinarySearchTreeNode* current_node)
函数来测试给定节点是否为叶节点。因此,计数器函数可以简单地遍历树并查找所有叶节点,一旦完成就返回最终计数。
如果要在没有递归的情况下测量不平衡树的最大深度,您将在树的insert()
函数中需要跟踪节点插入的深度。这可以简单地是在node
类型中的变量,该变量在节点插入树中时设置。然后,您可以遍历这三个,并找到叶节点的最大深度。
答案 3 :(得分:1)
您可能需要在插入时计算此值。存储节点的高度,即在Node对象中添加一个整数字段,如height。还有树的计数器高度和叶子。插入节点时,如果其父节点是叶子,则叶子计数不会更改,但如果不是,则将叶子计数增加1.此外,新节点的高度为父节点的高度+ 1,因此如果更大比树的当前高度,然后更新它。它是一个功课,所以我不会帮助实际的代码
答案 4 :(得分:1)
偶尔平衡你的树。如果您的树在FindHeight()上获得stackoverflow,则表示您的树方式不平衡。如果树是平衡的,那么对于100000个元素,它应该只有大约20个节点的深度。
重新平衡非平衡二叉树的最简单(但相当慢)的方法是分配一个足够大的TItem
数组来保存树中的所有数据,将所有数据插入到排序中订购并删除所有节点。然后递归地从数组重建树。根是中间的节点。 root->left
是左半部分的中间,root->right
是右半部分的中间。递归重复。这是重新平衡的最简单方法,但它很慢并且暂时占用大量内存。另一方面,当您检测到树非常不平衡时(插入深度超过100),您只需执行此操作。
另一个(更好)选项是在插入期间进行平衡。最直观的方法是跟踪当前节点下面有多少节点。如果正确的孩子的“孩子”节点数是左孩子的两倍多,则“向左”旋转。反之亦然。有关如何在互联网上进行树旋转的问题。这使得插入稍慢,但是你没有第一个选项创建的偶然的大量停顿。另一方面,你必须在旋转时不断更新所有“子”计数,这不是一件容易的事。