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

C语言中AVL树的性能#

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

    我在C中实现了一个AVL树,其插入矩阵如下

    Number of Elements          Time taken to insert (sec)
    ------------------------------------------------------
    10                        0.067
    100                       0.073
    200                       0.112
    500                       0.388
    900                       1.205
    1000                          1.466
    5000                         44.314
    10000                       195.435
    

    现在我的问题是,对于一个AVL树来说,它是一个好的性能,还是我必须重新考虑更改算法或重构代码?


    编辑: 元素是从元素的0到开始的整数。 测试代码如下

       [Test]
        public void InsertionTest()
        {
            AVLTree<int> _tree = new AVLTree<int>();
            _stopWatch.Start();
            for (int i = 0; i < 5000; i++) {
                _tree.Add(i);
            }
            _stopWatch.Stop();
    
            Console.WriteLine("Time taken = " + _stopWatch.Elapsed);
        }   
    

    编辑:实现代码

    双晶树

    [Serializable]
        public class BinarySearchTree<T> : ICollection<T>
        {
            private readonly Comparer<T> _comparer = Comparer<T>.Default;
    
            public BinarySearchTree()
            {
            }
    
            public BinarySearchTree(IEnumerable<T> collection)
            {
                AddRange(collection.ToArray());
            }
    
            public BinarySearchTree(Comparer<T> comparer)
            {
                _comparer = comparer;
            }
    
            public BinaryTreeNode<T> Root { get; protected set; }
    
            #region ICollection<T> Members
    
            /// <summary>
            ///   Adds an item to the <see cref = "T:System.Collections.Generic.ICollection`1" />.
            /// </summary>
            /// <param name = "value">The object to add to the <see cref = "T:System.Collections.Generic.ICollection`1" />.
            /// </param>
            /// <exception cref = "T:System.NotSupportedException">The <see cref = "T:System.Collections.Generic.ICollection`1" /> is read-only.
            /// </exception>
            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;
                    }
                }
    
                Count++;
                if (parent == null)
                    Root = n;
                else
                {
                    result = _comparer.Compare(parent.Value, value);
                    if (result > 0)
                        parent.Left = n;
                    else
                        parent.Right = n;
                }
            }
    
            /// <summary>
            ///   Removes all items from the <see cref = "T:System.Collections.Generic.ICollection`1" />.
            /// </summary>
            /// <exception cref = "T:System.NotSupportedException">The <see cref = "T:System.Collections.Generic.ICollection`1" /> is read-only. 
            /// </exception>
            public void Clear()
            {
                Root = null;
                Count = 0;
            }
    
            /// <summary>
            ///   Determines whether the <see cref = "T:System.Collections.Generic.ICollection`1" /> contains a specific value.
            /// </summary>
            /// <returns>
            ///   true if <paramref name = "item" /> is found in the <see cref = "T:System.Collections.Generic.ICollection`1" />; otherwise, false.
            /// </returns>
            /// <param name = "item">The object to locate in the <see cref = "T:System.Collections.Generic.ICollection`1" />.
            /// </param>
            public virtual bool Contains(T item)
            {
                BinaryTreeNode<T> current = Root;
                while (current != null)
                {
                    int result = _comparer.Compare(current.Value, item);
                    if (result == 0)
                        return true;
                    if (result > 0)
                        current = current.Left;
                    else if (result < 0)
                        current = current.Right;
                }
    
                return false;
            }
    
            public void CopyTo(T[] array, int index)
            {
                CopyTo(array, index, BinaryTreeTraversalType.InOrder);
            }
    
            /// <summary>
            ///   Removes the first occurrence of a specific object from the <see cref = "T:System.Collections.Generic.ICollection`1" />.
            /// </summary>
            /// <returns>
            ///   true if <paramref name = "item" /> was successfully removed from the <see cref = "T:System.Collections.Generic.ICollection`1" />; otherwise, false. This method also returns false if <paramref name = "item" /> is not found in the original <see cref = "T:System.Collections.Generic.ICollection`1" />.
            /// </returns>
            /// <param name = "item">The object to remove from the <see cref = "T:System.Collections.Generic.ICollection`1" />.
            /// </param>
            /// <exception cref = "T:System.NotSupportedException">The <see cref = "T:System.Collections.Generic.ICollection`1" /> is read-only.
            /// </exception>
            public virtual bool Remove(T item)
            {
                if (Root == null)
                    return false;
    
                BinaryTreeNode<T> current = Root, parent = null;
                int result = _comparer.Compare(current.Value, item);
                while (result != 0)
                {
                    if (result > 0)
                    {
                        parent = current;
                        current = current.Left;
                    }
                    else if (result < 0)
                    {
                        parent = current;
                        current = current.Right;
                    }
    
                    if (current == null)
                        return false;
                    result = _comparer.Compare(current.Value, item);
                }
    
                Count--;
    
                // We now need to "rethread" the tree
                // CASE 1: If current has no right child, then current's left child becomes
                //         the node pointed to by the parent
                if (current.Right == null)
                {
                    if (parent == null)
                        Root = current.Left;
                    else
                    {
                        result = _comparer.Compare(parent.Value, current.Value);
                        if (result > 0)
                            parent.Left = current.Left;
                        else if (result < 0)
                            parent.Right = current.Left;
                    }
    
                    // CASE 2: If current's right child has no left child, then current's right child
                    //         replaces current in the tree
                }
                else if (current.Right.Left == null)
                {
                    current.Right.Left = current.Left;
    
                    if (parent == null)
                        Root = current.Right;
                    else
                    {
                        result = _comparer.Compare(parent.Value, current.Value);
                        if (result > 0)
                            parent.Left = current.Right;
                        else if (result < 0)
                            parent.Right = current.Right;
                    }
    
                    // CASE 3: If current's right child has a left child, replace current with current's
                    //          right child's left-most descendent
                }
                else
                {
                    BinaryTreeNode<T> leftmost = current.Right.Left, lmParent = current.Right;
                    while (leftmost.Left != null)
                    {
                        lmParent = leftmost;
                        leftmost = leftmost.Left;
                    }
    
                    lmParent.Left = leftmost.Right;
    
                    leftmost.Left = current.Left;
                    leftmost.Right = current.Right;
    
                    if (parent == null)
                        Root = leftmost;
                    else
                    {
                        result = _comparer.Compare(parent.Value, current.Value);
                        if (result > 0)
                            parent.Left = leftmost;
                        else if (result < 0)
                            parent.Right = leftmost;
                    }
                }
    
                current.Left = current.Right = null;
    
                return true;
            }
    
            /// <summary>
            ///   Gets the number of elements contained in the <see cref = "T:System.Collections.Generic.ICollection`1" />.
            /// </summary>
            /// <returns>
            ///   The number of elements contained in the <see cref = "T:System.Collections.Generic.ICollection`1" />.
            /// </returns>
            public int Count { get; private set; }
    
            /// <summary>
            ///   Gets a value indicating whether the <see cref = "T:System.Collections.Generic.ICollection`1" /> is read-only.
            /// </summary>
            /// <returns>
            ///   true if the <see cref = "T:System.Collections.Generic.ICollection`1" /> is read-only; otherwise, false.
            /// </returns>
            public bool IsReadOnly
            {
                get { return false; }
            }
    
            #endregion
    
            public void AddRange(IEnumerable<T> items)
            {
                foreach (var item in items)
                {
                    Add(item);
                }
            }
    
            public void CopyTo(T[] array, int index, BinaryTreeTraversalType traversalType)
            {
                Root.ToEnumerable(traversalType).Select(x => x.Value).ToArray().CopyTo(array, index);
            }
    
            public BinaryTreeNode<T> Find(T value)
            {
                BinaryTreeNode<T> current = Root;
                while (current != null)
                {
                    int result = _comparer.Compare(current.Value, value);
                    if (result == 0)
                        return current;
                    if (result > 0)
                        current = current.Left;
                    else if (result < 0)
                        current = current.Right;
                }
    
                return null;
            }
    
            #region Implementation of IEnumerable
    
            /// <summary>
            ///   Returns an enumerator that iterates through the collection.
            /// </summary>
            /// <returns>
            ///   A <see cref = "T:System.Collections.Generic.IEnumerator`1" /> that can be used to iterate through the collection.
            /// </returns>
            /// <filterpriority>1</filterpriority>
            public IEnumerator<T> GetEnumerator()
            {
                return Root.ToEnumerable(BinaryTreeTraversalType.InOrder).Select(x => x.Value).GetEnumerator();
            }
    
            /// <summary>
            ///   Returns an enumerator that iterates through a collection.
            /// </summary>
            /// <returns>
            ///   An <see cref = "T:System.Collections.IEnumerator" /> object that can be used to iterate through the collection.
            /// </returns>
            /// <filterpriority>2</filterpriority>
            IEnumerator IEnumerable.GetEnumerator()
            {
                return GetEnumerator();
            }
    
            #endregion
        }
    

    AVLTER

    public class AVLTree<T> : BinarySearchTree<T>
        {
            public AVLTree()
            {
            }
    
            public AVLTree(IEnumerable<T> collection)
                : base(collection)
            {
            }
    
            public AVLTree(Comparer<T> comparer)
                : base(comparer)
            {
            }
    
    
            public override void Add(T value)
            {
                base.Add(value);
                var node = Find(value);
    
                AbstractNode<T> parentNode = node.Parent;
    
                while (parentNode != null)
                {
                    int balance = GetBalance(parentNode as BinaryTreeNode<T>);
                    if (Math.Abs(balance) == 2)
                    {
                        BalanceAt(parentNode as BinaryTreeNode<T>, balance);
                    }
    
                    parentNode = parentNode.Parent;
                }
            }
    
            public override bool Remove(T item)
            {
                if (Root == null)
                    return false;
    
                BinaryTreeNode<T> valueNode = Find(item);
                AbstractNode<T> parentNode = valueNode.Parent;
    
                bool removed = base.Remove(item);
    
                if (!removed)
                    return false;
    
                while (parentNode != null)
                {
                    int balance = GetBalance(parentNode as BinaryTreeNode<T>);
    
                    if (Math.Abs(balance) == 1)
                        break;
                    if (Math.Abs(balance) == 2)
                    {
                        BalanceAt(parentNode as BinaryTreeNode<T>, balance);
                    }
    
                    parentNode = parentNode.Parent;
                }
    
                return true;
            }
    
            /// <summary>
            /// Balances an AVL Tree node
            /// </summary>
            protected virtual void BalanceAt(BinaryTreeNode<T> node, int balance)
            {
                if (balance == 2)
                {
                    int rightBalance = GetBalance(node.Right);
    
                    if (rightBalance == 1 || rightBalance == 0)
                    {
                        RotateLeft(node);
                    }
                    else if (rightBalance == -1)
                    {
                        RotateRight(node.Right);
                        RotateLeft(node);
                    }
                }
                else if (balance == -2)
                {
                    int leftBalance = GetBalance(node.Left);
                    if (leftBalance == 1)
                    {
                        RotateLeft(node.Left);
                        RotateRight(node);
                    }
                    else if (leftBalance == -1 || leftBalance == 0)
                    {
                        RotateRight(node);
                    }
                }
            }
    
            /// <summary>
            /// Determines the balance of a given node
            /// </summary>
            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);
    
    // ReSharper disable AssignNullToNotNullAttribute
                    var leftHeight = leftSubtree.IsNullOrEmpty() ? 0 : leftSubtree.Max(x => x.Depth) - node.Depth;
                    var righHeight = righSubtree.IsNullOrEmpty() ? 0 : righSubtree.Max(x => x.Depth) - node.Depth;
    // ReSharper restore AssignNullToNotNullAttribute
    
                    return righHeight - leftHeight;
                }
                return 0;            
            }
    
            /// <summary>
            /// Rotates a node to the left within an AVL Tree
            /// </summary>
            protected virtual void RotateLeft(BinaryTreeNode<T> node)
            {
                if (node == null)
                    return;
    
                BinaryTreeNode<T> pivot = node.Right;
    
                if (pivot == null)
                    return;
                var rootParent = node.Parent as BinaryTreeNode<T>;
                bool isLeftChild = (rootParent != null) && rootParent.Left == node;
                bool makeTreeRoot = node == Root;
    
                node.Right = pivot.Left;
                pivot.Left = node;
    
                node.Parent = pivot;
                pivot.Parent = rootParent;
    
                if (node.Right != null)
                    node.Right.Parent = node;
    
                if (makeTreeRoot)
                    Root = pivot;
    
                if (isLeftChild)
                    rootParent.Left = pivot;
                else if (rootParent != null)
                    rootParent.Right = pivot;
            }
    
            /// <summary>
            /// Rotates a node to the right within an AVL Tree
            /// </summary>
            protected virtual void RotateRight(BinaryTreeNode<T> node)
            {
                if (node == null)
                    return;
    
                BinaryTreeNode<T> pivot = node.Left;
    
                if (pivot == null)
                    return;
                var rootParent = node.Parent as BinaryTreeNode<T>;
                bool isLeftChild = (rootParent != null) && rootParent.Left == node;
                bool makeTreeRoot = Root == node; 
    
                node.Left = pivot.Right;
                pivot.Right = node;
    
                node.Parent = pivot;
                pivot.Parent = rootParent;
    
                if (node.Left != null)
                    node.Left.Parent = node;
    
                if (makeTreeRoot)
                    Root = pivot;
                if (isLeftChild)
                    rootParent.Left = pivot;
                else if (rootParent != null)
                    rootParent.Right = pivot;
            }
        }
    
    2 回复  |  直到 14 年前
        1
  •  0
  •   LBushkin    16 年前

    .NET中的基准代码要求您考虑JIT编译时间。最简单的方法是让测试代码运行两次代码,并丢弃第一次计时的结果。

    除此之外-您的基准结果似乎意味着增长率比AVL树插入的预期O(log n)时间更差。

    你在这里没有发布的是 BinaryTreeNode<> 班级。我怀疑您在实现那里的一些方法时可能有问题-我特别怀疑 Depth 和 ToEnumerable .

        2
  •  2
  •   Henk Holterman    16 年前

    如果我看100/500和1000/5000,我会(非常粗略地)看到时间增加了5倍。不可能说出这是O(n)还是O(nlogn)。

    但当我看到5.000和10.000时,我也看到了几乎5倍的增长。这让我怀疑你的基准代码。

    不管怎样,没有代码,这几乎不是一个真正的问题。至少展示一下你是如何测量的。


    代码太多了,但我想这是您确定节点平衡的(迭代)方法。传统的AVL树将此缓存在成员中(并使其保持最新)。