遍历BST时的Stackoverflow异常

时间:2011-11-09 23:52:28

标签: c++ recursion stack-overflow binary-search-tree

我在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);
}

我试着想一想如何在没有递归的情况下实现这两种方法,但我完全被难倒了。有人有什么想法吗?

5 个答案:

答案 0 :(得分:3)

如果平衡,则具有100,000个节点的树上的递归应该不是问题。深度可能只有17,在所示的实现中不会使用非常多的堆栈。 (log2(100,000) = 16.61)。所以似乎构建树的代码可能没有正确平衡它。

答案 1 :(得分:2)

我发现this page非常具有启发性,因为它讨论了将使用递归的函数转换为使用迭代的函数的机制。

它还有显示代码的示例。

答案 2 :(得分:1)

为了计算没有递归的叶子,使用迭代器的概念,如STL用于基础std::setstd::map的RB树...创建begin()end()函数为您的树识别有序的第一个和最后一个节点(在这种情况下是最左边的节点,然后是最右边的节点)。然后创建一个名为

的函数

BinarySearchTreeNode* increment(const BinarySearchTreeNode* current_node)

对于给定的current_node,将返回指向树中下一个节点的指针。请记住,要使此实现正常工作,您需要在parent类型中添加额外的node指针以辅助迭代过程。

increment()的算法类似于以下内容:

  1. 检查当前节点是否有右子。
  2. 如果有右子,请使用while循环查找该右子树的最左侧节点。这将是“下一个”节点。否则请转到步骤#3。
  3. 如果当前节点上没有右子节点,则检查当前节点是否是其父节点的左子节点。
  4. 如果步骤#3为真,则“下一个”节点是父节点,因此您可以在此时停止,否则请执行下一步。
  5. 如果步骤#3为假,则当前节点是父节点的右子节点。因此,您需要使用while循环继续向上移动到下一个父节点,直到遇到其父节点的左子节点。这个左子节点的父节点将成为“下一个”节点,您可以停止。
  6. 最后,如果步骤#5返回到根目录,那么当前节点是树中的最后一个节点,并且迭代器已到达树的末尾。
  7. 最后,您需要一个bool leaf(const BinarySearchTreeNode* current_node)函数来测试给定节点是否为叶节点。因此,计数器函数可以简单地遍历树并查找所有叶节点,一旦完成就返回最终计数。

    如果要在没有递归的情况下测量不平衡树的最大深度,您将在树的insert()函数中需要跟踪节点插入的深度。这可以简单地是在node类型中的变量,该变量在节点插入树中时设置。然后,您可以遍历这三个,并找到叶节点的最大深度。

    顺便说一下,不幸的是,这种方法的复杂性将是O(N)......远远不如O(log N)。

答案 3 :(得分:1)

您可能需要在插入时计算此值。存储节点的高度,即在Node对象中添加一个整数字段,如height。还有树的计数器高度和叶子。插入节点时,如果其父节点是叶子,则叶子计数不会更改,但如果不是,则将叶子计数增加1.此外,新节点的高度为父节点的高度+ 1,因此如果更大比树的当前高度,然后更新它。它是一个功课,所以我不会帮助实际的代码

答案 4 :(得分:1)

偶尔平衡你的树。如果您的树在FindHeight()上获得stackoverflow,则表示您的树方式不平衡。如果树是平衡的,那么对于100000个元素,它应该只有大约20个节点的深度。

重新平衡非平衡二叉树的最简单(但相当慢)的方法是分配一个足够大的TItem数组来保存树中的所有数据,将所有数据插入到排序中订购并删除所有节点。然后递归地从数组重建树。根是中间的节点。 root->left是左半部分的中间,root->right是右半部分的中间。递归重复。这是重新平衡的最简单方法,但它很慢并且暂时占用大量内存。另一方面,当您检测到树非常不平衡时(插入深度超过100),您只需执行此操作。

另一个(更好)选项是在插入期间进行平衡。最直观的方法是跟踪当前节点下面有多少节点。如果正确的孩子的“孩子”节点数是左孩子的两倍多,则“向左”旋转。反之亦然。有关如何在互联网上进行树旋转的问题。这使得插入稍慢,但是你没有第一个选项创建的偶然的大量停顿。另一方面,你必须在旋转时不断更新所有“子”计数,这不是一件容易的事。