代码之家  ›  专栏  ›  技术社区  ›  Anindya Chatterjee

如何避免此stackoverflow异常?

  •  5
  • Anindya Chatterjee  · 技术社区  · 16 年前

    这里是情况,我正在开发一个二叉搜索树,在树的每个节点中,我打算存储它自己的高度,以便在AVL树形成期间进一步平衡树。以前我有一个迭代的方法来计算平衡树期间节点的高度,如下所示。

    (以下代码属于名为 AVLTree<T> 哪个是儿童班 BinarySearchTree<T> )

    protected virtual int GetBalance(BinaryTreeNode<T> node)
            {
                if(node != null)
                {
                    IEnumerable<BinaryTreeNode<T>> leftSubtree = null, righSubtree = null;
    
                    if (node.Left != null)
                        leftSubtree = node.Left.ToEnumerable(BinaryTreeTraversalType.InOrder);
    
                    if (node.Right != null)
                        righSubtree = node.Right.ToEnumerable(BinaryTreeTraversalType.InOrder);
    
    
                    var leftHeight = leftSubtree.IsNullOrEmpty() ? 0 : leftSubtree.Max(x => x.Depth) - node.Depth;
                    var righHeight = righSubtree.IsNullOrEmpty() ? 0 : righSubtree.Max(x => x.Depth) - node.Depth;
    
    
                    return righHeight - leftHeight;
                }
                return 0;            
            }
    

    但是它会产生大量的性能开销。

    Performance of an AVL Tree in C#

    因此,在插入到 二进制搜索树 . 现在,在平衡过程中,我能够避免这个迭代,并且在 avltree<t> .

    但现在的问题是,如果我试图插入大量的数据,比如1-50000个 二进制搜索树 (不平衡它),我得到stackOverflowException。我正在提供导致它的代码。你能帮我找到一个解决方案来避免这个例外,同时也不会影响它的子类的性能吗? avltree<t> ?

    public class BinaryTreeNode<T>
        {
            private BinaryTreeNode<T> _left, _right;
            private int _height;
    
            public T Value {get; set; }
            public BinaryTreeNode<T> Parent;
            public int Depth {get; set; }
    
            public BinaryTreeNode()
            {}
    
            public BinaryTreeNode(T data)
            {
                Value = data;
            }
    
            public BinaryTreeNode<T> Left
            {
                get { return _left; }
                set
                {
                    _left = value;
                    if (_left != null)
                    {
                        _left.Depth = Depth + 1;    
                        _left.Parent = this;
                    }                
                    UpdateHeight();
                }
            }
    
            public BinaryTreeNode<T> Right
            {
                get { return _right; }
                set
                {
                    _right = value;
                    if (_right != null)
                    {
                        _right.Depth = Depth + 1;
                        _right.Parent = this;
                    }
                    UpdateHeight();
                }
            }
    
            public int Height
            {
                get { return _height; }
                protected internal set
                {
                    _height = value;
                    if (Parent != null) {
                        Parent.UpdateHeight();
                    }               
                }
            }
    
            private void UpdateHeight()
            {
                if (Left == null && Right == null) {
                    return;
                }
                if(Left != null && Right != null)
                {
                    if (Left.Height > Right.Height)
                        Height = Left.Height + 1;
                    else
                        Height = Right.Height + 1;
                }
                else if(Left == null)
                    Height = Right.Height + 1;
                else
                    Height = Left.Height + 1;
            }
    
        }
    

    public class BinarySearchTree<T>
        {
            private readonly Comparer<T> _comparer = Comparer<T>.Default;
    
            public BinarySearchTree()
            {
            }
    
            public BinaryTreeNode<T> Root {get; set;}
    
            public virtual void Add(T value)
            {
                var n = new BinaryTreeNode<T>(value);
                int result;
    
                BinaryTreeNode<T> current = Root, parent = null;
                while (current != null)
                {
                    result = _comparer.Compare(current.Value, value);
                    if (result == 0)
                    {
                        parent = current;
                        current = current.Left;
                    }
                    if (result > 0)
                    {
                        parent = current;
                        current = current.Left;
                    }
                    else if (result < 0)
                    {
                        parent = current;
                        current = current.Right;
                    }
                }
    
                if (parent == null)
                    Root = n;
                else
                {
                    result = _comparer.Compare(parent.Value, value);
                    if (result > 0)
                        parent.Left = n;
                    else
                        parent.Right = n;
                }
            }
        }
    

    我在计算下一行的高度时得到stackOverflowException

    if (Parent != null) {
                        Parent.UpdateHeight();
                    } 
    

    Height 性质 BinaryTreeNode<T> 班级。如果可能的话,请建议我找点工作。

    顺便说一句,非常感谢您注意阅读这么长的问题:)

    4 回复  |  直到 16 年前
        1
  •  2
  •   Martin Liversage    16 年前

    添加节点时,通过递归地遍历所有父节点来计算高度。.NET进程的堆栈空间有限,如果给定一个大的树,您将消耗所有堆栈空间并获得 StackOverflowException . 可以将递归更改为迭代,以避免占用堆栈空间。其他语言,如函数语言,通过使用一种称为尾部递归的技术,可以在不消耗堆栈空间的情况下进行递归。但是,在C中,您必须手动修改代码。

    以下是的修改版本 Height UpdateHeight 在里面 BinaryTreeNode<T> 它不使用递归:

    public int Height {
      get { return _height; }
      private set { _height = value; }
    }
    
    void UpdateHeight() {
      var leftHeight = Left != null ? Left.Height + 1 : 0;
      var rightHeight = Right != null ? Right.Height + 1 : 0;
      var height = Math.Max(leftHeight, rightHeight);
      var node = this;
      while (node != null) {
        node.Height = height;
        height += 1;
        node = node.Parent;
      }
    }
    
        2
  •  0
  •   BartoszAdamczewski    16 年前

    你可以加一个尾巴。调用il,解压缩文件,然后重新编译。

    例子:

    …ILY002:添加

    尾巴。

    IL_:打电话…

    ILY008:RET

    再次编译它的示例:

    ILASM C:\test.il/out=C:\testtail.exe

    (这可能不是你想要的,但也是一个例子)

    我相信你能弄明白,让它发挥作用,这并不难。

    最大的缺点是重新编译将消除尾部调用,因此我建议在msbuild中设置一个生成任务,以便为您自动执行该任务。

        3
  •  0
  •   Anindya Chatterjee    16 年前

    我想我找到了解决方案,我修改了下面的代码,它很有魅力

    public int Height
            {
                get { return _height; }
                protected internal set
                {
                    _height = value;                                
                }
            }
    
            private void UpdateHeight()
            {
                if (Left == null && Right == null) {
                    return;
                }
                if(Left != null && Right != null)
                {
                    if (Left.Height > Right.Height)
                        Height = Left.Height + 1;
                    else
                        Height = Right.Height + 1;
                }
                else if(Left == null)
                    Height = Right.Height + 1;
                else
                    Height = Left.Height + 1;
    
                var parent = Parent;
                while (parent != null) {
                    parent.Height++;
                    parent = parent.Parent;             
                }           
    
            }
    

    感谢那些花时间让我找出解决办法的人。

        4
  •  0
  •   TedTrippin    16 年前

    如果您一次插入大量数据,我认为您最好在不调用parent.updateHeight的情况下批量插入数据,然后在移动时遍历树设置高度。

    添加未来的节点,我将在树上行走,从根开始,并在移动时增加高度。