Red Black Tree : Tree « Collections Data Structure « C# / C Sharp






Red Black Tree

 
//******************************
// Written by Peter Golde
// Copyright (c) 2004-2007, Wintellect
//
// Use and restribution of this code is subject to the license agreement 
// contained in the file "License.txt" accompanying this file.
//******************************

using System;
using System.Diagnostics;
using System.Collections.Generic;


namespace Wintellect.PowerCollections
{
    /// <summary>
    /// Describes what to do if a key is already in the tree when doing an
    /// insertion.
    /// </summary>
    internal enum DuplicatePolicy
    {
        InsertFirst,               // Insert a new node before duplicates
        InsertLast,               // Insert a new node after duplicates
        ReplaceFirst,            // Replace the first of the duplicate nodes
        ReplaceLast,            // Replace the last of the duplicate nodes
        DoNothing                // Do nothing to the tree
    };

    /// <summary>
    /// The base implementation for various collections classes that use Red-Black trees
    /// as part of their implementation. This class should not (and can not) be 
    /// used directly by end users; it's only for internal use by the collections package.
    /// </summary>
    /// <remarks>
    /// The Red-Black tree manages items of type T, and uses a IComparer&lt;T&gt; that
    /// compares items to sort the tree. Multiple items can compare equal and be stored
    /// in the tree. Insert, Delete, and Find operations are provided in their full generality;
    /// all operations allow dealing with either the first or last of items that compare equal. 
    ///</remarks>
    internal class RedBlackTree<T> : IEnumerable<T>
    {
        private readonly IComparer<T> comparer;      // interface for comparing elements, only Compare is used.
        private Node root;          // The root of the tree. Can be null when tree is empty.
        private int count;            // The count of elements in the tree.

        private int changeStamp;        // An integer that is changed every time the tree structurally changes.
        // Used so that enumerations throw an exception if the tree is changed
        // during enumeration.

        private Node[] stack;               // A stack of nodes. This is cached locally to avoid constant re-allocated it.

        /// <summary>
        /// Create an array of Nodes big enough for any path from top 
        /// to bottom. This is cached, and reused from call-to-call, so only one
        /// can be around at a time per tree.
        /// </summary>
        /// <returns>The node stack.</returns>
        private Node[] GetNodeStack()
        {
            // Maximum depth needed is 2 * lg count + 1.
            int maxDepth;
            if (count < 0x400)
                maxDepth = 21;
            else if (count < 0x10000)
                maxDepth = 41;
            else
                maxDepth = 65;

            if (stack == null || stack.Length < maxDepth)
                stack = new Node[maxDepth];

            return stack;
        }

        /// <summary>
        /// The class that is each node in the red-black tree.
        /// </summary>
        private class Node
        {
            public Node left, right;
            public T item;

            private const uint REDMASK = 0x80000000;
            private uint count;

            /// <summary>
            /// Is this a red node?
            /// </summary>
            public bool IsRed
            {
                get { return (count & REDMASK) != 0; }
                set
                {
                    if (value)
                        count |= REDMASK;
                    else
                        count &= ~REDMASK;
                }
            }

            /// <summary>
            /// Get or set the Count field -- a 31-bit field
            /// that holds the number of nodes at or below this
            /// level.
            /// </summary>
            public int Count
            {
                get { return (int)(count & ~REDMASK); }
                set { count = (count & REDMASK) | (uint)value; }
            }

            /// <summary>
            /// Add one to the Count.
            /// </summary>
            public void IncrementCount()
            {
                ++count;
            }

            /// <summary>
            /// Subtract one from the Count. The current
            /// Count must be non-zero.
            /// </summary>
            public void DecrementCount()
            {
                Debug.Assert(Count != 0);
                --count;
            }

            /// <summary>
            /// Clones a node and all its descendants.
            /// </summary>
            /// <returns>The cloned node.</returns>
            public Node Clone()
            {
                Node newNode = new Node();
                newNode.item = item;

                newNode.count = count;

                if (left != null)
                    newNode.left = left.Clone();

                if (right != null)
                    newNode.right = right.Clone();

                return newNode;
            }
        }

        /// <summary>
        /// Must be called whenever there is a structural change in the tree. Causes
        /// changeStamp to be changed, which causes any in-progress enumerations
        /// to throw exceptions.
        /// </summary>
        internal void StopEnumerations()
        {
            ++changeStamp;
        }

        /// <summary>
        /// Checks the given stamp against the current change stamp. If different, the
        /// collection has changed during enumeration and an InvalidOperationException
        /// must be thrown
        /// </summary>
        /// <param name="startStamp">changeStamp at the start of the enumeration.</param>
        private void CheckEnumerationStamp(int startStamp)
        {
            if (startStamp != changeStamp)
            {
                throw new InvalidOperationException("Change During Enumeration");
            }
        }

        /// <summary>
        /// Initialize a red-black tree, using the given interface instance to compare elements. Only
        /// Compare is used on the IComparer interface.
        /// </summary>
        /// <param name="comparer">The IComparer&lt;T&gt; used to sort keys.</param>
        public RedBlackTree(IComparer<T> comparer)
        {
            this.comparer = comparer;
            this.count = 0;
            this.root = null;
        }

        /// <summary>
        /// Returns the number of elements in the tree.
        /// </summary>
        public int ElementCount
        {
            get
            {
                return count;
            }
        }

        /// <summary>
        /// Clone the tree, returning a new tree containing the same items. Should
        /// take O(N) take.
        /// </summary>
        /// <returns>Clone version of this tree.</returns>
        public RedBlackTree<T> Clone()
        {
            RedBlackTree<T> newTree = new RedBlackTree<T>(comparer);
            newTree.count = this.count;
            if (this.root != null)
                newTree.root = this.root.Clone();
            return newTree;
        }

        /// <summary>
        /// Finds the key in the tree. If multiple items in the tree have
        /// compare equal to the key, finds the first or last one. Optionally replaces the item
        /// with the one searched for.
        /// </summary>
        /// <param name="key">Key to search for.</param>
        /// <param name="findFirst">If true, find the first of duplicates, else finds the last of duplicates.</param>
        /// <param name="replace">If true, replaces the item with key (if function returns true)</param>
        /// <param name="item">Returns the found item, before replacing (if function returns true).</param>
        /// <returns>True if the key was found.</returns>
        public bool Find(T key, bool findFirst, bool replace, out T item)
        {
            Node current = root;      // current search location in the tree
            Node found = null;      // last node found with the key, or null if none.

            while (current != null)
            {
                int compare = comparer.Compare(key, current.item);

                if (compare < 0)
                {
                    current = current.left;
                }
                else if (compare > 0)
                {
                    current = current.right;
                }
                else
                {
                    // Go left/right on equality to find first/last of elements with this key.
                    Debug.Assert(compare == 0);
                    found = current;
                    if (findFirst)
                        current = current.left;
                    else
                        current = current.right;
                }
            }

            if (found != null)
            {
                item = found.item;
                if (replace)
                    found.item = key;
                return true;
            }
            else
            {
                item = default(T);
                return false;
            }
        }

        /// <summary>
        /// Finds the index of the key in the tree. If multiple items in the tree have
        /// compare equal to the key, finds the first or last one. 
        /// </summary>
        /// <param name="key">Key to search for.</param>
        /// <param name="findFirst">If true, find the first of duplicates, else finds the last of duplicates.</param>
        /// <returns>Index of the item found if the key was found, -1 if not found.</returns>
        public int FindIndex(T key, bool findFirst)
        {
            T dummy;
            if (findFirst)
                return FirstItemInRange(EqualRangeTester(key), out dummy);
            else
                return LastItemInRange(EqualRangeTester(key), out dummy);
        }

        /// <summary>
        /// Find the item at a particular index in the tree.
        /// </summary>
        /// <param name="index">The zero-based index of the item. Must be &gt;= 0 and &lt; Count.</param>
        /// <returns>The item at the particular index.</returns>
        public T GetItemByIndex(int index)
        {
            if (index < 0 || index >= count)
                throw new ArgumentOutOfRangeException("index");

            Node current = root;      // current search location in the tree

            for (; ; )
            {
                int leftCount;

                if (current.left != null)
                    leftCount = current.left.Count;
                else
                    leftCount = 0;

                if (leftCount > index)
                    current = current.left;
                else if (leftCount == index)
                    return current.item;
                else
                {
                    index -= leftCount + 1;
                    current = current.right;
                }
            }
        }

        /// <summary>
        /// Insert a new node into the tree, maintaining the red-black invariants.
        /// </summary>
        /// <remarks>Algorithm from Sedgewick, "Algorithms".</remarks>
        /// <param name="item">The new item to insert</param>
        /// <param name="dupPolicy">What to do if equal item is already present.</param>
        /// <param name="previous">If false, returned, the previous item.</param>
        /// <returns>false if duplicate exists, otherwise true.</returns>
        public bool Insert(T item, DuplicatePolicy dupPolicy, out T previous)
        {
            Node node = root;
            Node parent = null, gparent = null, ggparent = null;  // parent, grand, a great-grantparent of node.
            bool wentLeft = false, wentRight = false;        // direction from parent to node.
            bool rotated;
            Node duplicateFound = null;

            // The tree may be changed.
            StopEnumerations();

            // We increment counts on the way down the tree. If we end up not inserting an items due
            // to a duplicate, we need a stack to adjust the counts back. We don't need the stack if the duplicate
            // policy means that we will always do an insertion.
            bool needStack = !((dupPolicy == DuplicatePolicy.InsertFirst) || (dupPolicy == DuplicatePolicy.InsertLast));
            Node[] nodeStack = null;
            int nodeStackPtr = 0;  // first free item on the stack.
            if (needStack)
                nodeStack = GetNodeStack();

            while (node != null)
            {
                // If we find a node with two red children, split it so it doesn't cause problems
                // when inserting a node.
                if (node.left != null && node.left.IsRed && node.right != null && node.right.IsRed)
                {
                    node = InsertSplit(ggparent, gparent, parent, node, out rotated);

                    if (needStack && rotated)
                    {
                        nodeStackPtr -= 2;
                        if (nodeStackPtr < 0)
                            nodeStackPtr = 0;
                    }
                }

                // Keep track of parent, grandparent, great-grand parent.
                ggparent = gparent; gparent = parent; parent = node;

                // Compare the key and the node. 
                int compare = comparer.Compare(item, node.item);

                if (compare == 0)
                {
                    // Found a node with the data already. Check duplicate policy.
                    if (dupPolicy == DuplicatePolicy.DoNothing)
                    {
                        previous = node.item;

                        // Didn't insert after all. Return counts back to their previous value.
                        for (int i = 0; i < nodeStackPtr; ++i)
                            nodeStack[i].DecrementCount();

                        return false;
                    }
                    else if (dupPolicy == DuplicatePolicy.InsertFirst || dupPolicy == DuplicatePolicy.ReplaceFirst)
                    {
                        // Insert first by treating the key as less than nodes in the tree.
                        duplicateFound = node;
                        compare = -1;
                    }
                    else
                    {
                        Debug.Assert(dupPolicy == DuplicatePolicy.InsertLast || dupPolicy == DuplicatePolicy.ReplaceLast);
                        // Insert last by treating the key as greater than nodes in the tree.
                        duplicateFound = node;
                        compare = 1;
                    }
                }

                Debug.Assert(compare != 0);

                node.IncrementCount();
                if (needStack)
                    nodeStack[nodeStackPtr++] = node;

                // Move to the left or right as needed to find the insertion point.
                if (compare < 0)
                {
                    node = node.left;
                    wentLeft = true; wentRight = false;
                }
                else
                {
                    node = node.right;
                    wentRight = true; wentLeft = false;
                }
            }

            if (duplicateFound != null)
            {
                previous = duplicateFound.item;

                // Are we replacing instread of inserting?
                if (dupPolicy == DuplicatePolicy.ReplaceFirst || dupPolicy == DuplicatePolicy.ReplaceLast)
                {
                    duplicateFound.item = item;

                    // Didn't insert after all. Return counts back to their previous value.
                    for (int i = 0; i < nodeStackPtr; ++i)
                        nodeStack[i].DecrementCount();

                    return false;
                }
            }
            else
            {
                previous = default(T);
            }

            // Create a new node.
            node = new Node();
            node.item = item;
            node.Count = 1;

            // Link the node into the tree.
            if (wentLeft)
                parent.left = node;
            else if (wentRight)
                parent.right = node;
            else
            {
                Debug.Assert(root == null);
                root = node;
            }

            // Maintain the red-black policy.
            InsertSplit(ggparent, gparent, parent, node, out rotated);

            // We've added a node to the tree, so update the count.
            count += 1;

            return (duplicateFound == null);
        }

        /// <summary>
        /// Split a node with two red children (a 4-node in the 2-3-4 tree formalism), as
        /// part of an insert operation.
        /// </summary>
        /// <param name="ggparent">great grand-parent of "node", can be null near root</param>
        /// <param name="gparent">grand-parent of "node", can be null near root</param>
        /// <param name="parent">parent of "node", can be null near root</param>
        /// <param name="node">Node to split, can't be null</param>
        /// <param name="rotated">Indicates that rotation(s) occurred in the tree.</param>
        /// <returns>Node to continue searching from.</returns>
        private Node InsertSplit(Node ggparent, Node gparent, Node parent, Node node, out bool rotated)
        {
            if (node != root)
                node.IsRed = true;
            if (node.left != null)
                node.left.IsRed = false;
            if (node.right != null)
                node.right.IsRed = false;

            if (parent != null && parent.IsRed)
            {
                // Since parent is red, gparent can't be null (root is always black). ggparent
                // might be null, however.
                Debug.Assert(gparent != null);

                // if links from gparent and parent are opposite (left/right or right/left),
                // then rotate.
                if ((gparent.left == parent) != (parent.left == node))
                {
                    Rotate(gparent, parent, node);
                    parent = node;
                }

                gparent.IsRed = true;

                // Do a rotate to prevent two red links in a row.
                Rotate(ggparent, gparent, parent);

                parent.IsRed = false;
                rotated = true;
                return parent;
            }
            else
            {
                rotated = false;
                return node;
            }
        }

        /// <summary>
        /// Performs a rotation involving the node, it's child and grandchild. The counts of 
        /// childs and grand-child are set the correct values from their children; this is important
        /// if they have been adjusted on the way down the try as part of an insert/delete.
        /// </summary>
        /// <param name="node">Top node of the rotation. Can be null if child==root.</param>
        /// <param name="child">One child of "node". Not null.</param>
        /// <param name="gchild">One child of "child". Not null.</param>
        private void Rotate(Node node, Node child, Node gchild)
        {
            if (gchild == child.left)
            {
                child.left = gchild.right;
                gchild.right = child;
            }
            else
            {
                Debug.Assert(gchild == child.right);
                child.right = gchild.left;
                gchild.left = child;
            }

            // Restore the counts.
            child.Count = (child.left != null ? child.left.Count : 0) + (child.right != null ? child.right.Count : 0) + 1;
            gchild.Count = (gchild.left != null ? gchild.left.Count : 0) + (gchild.right != null ? gchild.right.Count : 0) + 1;

            if (node == null)
            {
                Debug.Assert(child == root);
                root = gchild;
            }
            else if (child == node.left)
            {
                node.left = gchild;
            }
            else
            {
                Debug.Assert(child == node.right);
                node.right = gchild;
            }
        }

        /// <summary>
        /// Deletes a key from the tree. If multiple elements are equal to key, 
        /// deletes the first or last. If no element is equal to the key, 
        /// returns false.
        /// </summary>
        /// <remarks>Top-down algorithm from Weiss. Basic plan is to move down in the tree, 
        /// rotating and recoloring along the way to always keep the current node red, which 
        /// ensures that the node we delete is red. The details are quite complex, however! </remarks>
        /// <param name="key">Key to delete.</param>
        /// <param name="deleteFirst">Which item to delete if multiple are equal to key. True to delete the first, false to delete last.</param>
        /// <param name="item">Returns the item that was deleted, if true returned.</param>
        /// <returns>True if an element was deleted, false if no element had 
        /// specified key.</returns>
        public bool Delete(T key, bool deleteFirst, out T item)
        {
            return DeleteItemFromRange(EqualRangeTester(key), deleteFirst, out item);
        }

        /// 
        /// <summary>
        /// Enumerate all the items in-order
        /// </summary>
        /// <returns>An enumerator for all the items, in order.</returns>
        /// <exception cref="InvalidOperationException">The tree has an item added or deleted during the enumeration.</exception>
        public IEnumerator<T> GetEnumerator()
        {
            return EnumerateRange(EntireRangeTester).GetEnumerator();
        }

        /// <summary>
        /// Enumerate all the items in-order
        /// </summary>
        /// <returns>An enumerator for all the items, in order.</returns>
        /// <exception cref="InvalidOperationException">The tree has an item added or deleted during the enumeration.</exception>
        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        #region Ranges

        /// <summary>
        /// A delegate that tests if an item is within a custom range. The range must be a contiguous
        /// range of items with the ordering of this tree. The range test function must test
        /// if an item is before, withing, or after the range.
        /// </summary>
        /// <param name="item">Item to test against the range.</param>
        /// <returns>Returns negative if item is before the range, zero if item is withing the range,
        /// and positive if item is after the range.</returns>
        public delegate int RangeTester(T item);

        /// <summary>
        /// Gets a range tester that defines a range by first and last items.
        /// </summary>
        /// <param name="useFirst">If true, bound the range on the bottom by first.</param>
        /// <param name="first">If useFirst is true, the inclusive lower bound.</param>
        /// <param name="useLast">If true, bound the range on the top by last.</param>
        /// <param name="last">If useLast is true, the exclusive upper bound.</param>
        /// <returns>A RangeTester delegate that tests for an item in the given range.</returns>
        public RangeTester BoundedRangeTester(bool useFirst, T first, bool useLast, T last)
        {
            return delegate(T item)
            {
                if (useFirst && comparer.Compare(first, item) > 0)
                    return -1;     // item is before first.
                else if (useLast && comparer.Compare(last, item) <= 0)
                    return 1;      // item is after or equal to last.
                else
                    return 0;      // item is greater or equal to first, and less than last.
            };
        }

        /// <summary>
        /// Gets a range tester that defines a range by first and last items.
        /// </summary>
        /// <param name="first">The lower bound.</param>
        /// <param name="firstInclusive">True if the lower bound is inclusive, false if exclusive.</param>
        /// <param name="last">The upper bound.</param>
        /// <param name="lastInclusive">True if the upper bound is inclusive, false if exclusive.</param>
        /// <returns>A RangeTester delegate that tests for an item in the given range.</returns>
        public RangeTester DoubleBoundedRangeTester(T first, bool firstInclusive, T last, bool lastInclusive)
        {
            return delegate(T item)
            {
                if (firstInclusive)
                {
                    if (comparer.Compare(first, item) > 0)
                        return -1;     // item is before first.
                }
                else
                {
                    if (comparer.Compare(first, item) >= 0)
                        return -1;     // item is before or equal to first.
                }

                if (lastInclusive)
                {
                    if (comparer.Compare(last, item) < 0)
                        return 1;      // item is after last.
                }
                else
                {
                    if (comparer.Compare(last, item) <= 0)
                        return 1;      // item is after or equal to last
                }

                return 0;      // item is between first and last.
            };
        }


        /// <summary>
        /// Gets a range tester that defines a range by a lower bound.
        /// </summary>
        /// <param name="first">The lower bound.</param>
        /// <param name="inclusive">True if the lower bound is inclusive, false if exclusive.</param>
        /// <returns>A RangeTester delegate that tests for an item in the given range.</returns>
        public RangeTester LowerBoundedRangeTester(T first, bool inclusive)
        {
            return delegate(T item)
            {
                if (inclusive)
                {
                    if (comparer.Compare(first, item) > 0)
                        return -1;     // item is before first.
                    else
                        return 0;      // item is after or equal to first
                }
                else
                {
                    if (comparer.Compare(first, item) >= 0)
                        return -1;     // item is before or equal to first.
                    else
                        return 0;      // item is after first
                }
            };
        }


        /// <summary>
        /// Gets a range tester that defines a range by upper bound.
        /// </summary>
        /// <param name="last">The upper bound.</param>
        /// <param name="inclusive">True if the upper bound is inclusive, false if exclusive.</param>
        /// <returns>A RangeTester delegate that tests for an item in the given range.</returns>
        public RangeTester UpperBoundedRangeTester(T last, bool inclusive)
        {
            return delegate(T item)
            {
                if (inclusive)
                {
                    if (comparer.Compare(last, item) < 0)
                        return 1;      // item is after last.
                    else
                        return 0;      // item is before or equal to last.
                }
                else
                {
                    if (comparer.Compare(last, item) <= 0)
                        return 1;      // item is after or equal to last
                    else
                        return 0;      // item is before last.
                }
            };
        }

        /// <summary>
        /// Gets a range tester that defines a range by all items equal to an item.
        /// </summary>
        /// <param name="equalTo">The item that is contained in the range.</param>
        /// <returns>A RangeTester delegate that tests for an item equal to <paramref name="equalTo"/>.</returns>
        public RangeTester EqualRangeTester(T equalTo)
        {
            return delegate(T item)
            {
                return comparer.Compare(item, equalTo);
            };
        }

        /// <summary>
        /// A range tester that defines a range that is the entire tree.
        /// </summary>
        /// <param name="item">Item to test.</param>
        /// <returns>Always returns 0.</returns>
        public int EntireRangeTester(T item)
        {
            return 0;
        }

        /// <summary>
        /// Enumerate the items in a custom range in the tree. The range is determined by 
        /// a RangeTest delegate.
        /// </summary>
        /// <param name="rangeTester">Tests an item against the custom range.</param>
        /// <returns>An IEnumerable&lt;T&gt; that enumerates the custom range in order.</returns>
        /// <exception cref="InvalidOperationException">The tree has an item added or deleted during the enumeration.</exception>
        public IEnumerable<T> EnumerateRange(RangeTester rangeTester)
        {
            return EnumerateRangeInOrder(rangeTester, root);
        }

        /// <summary>
        /// Enumerate all the items in a custom range, under and including node, in-order.
        /// </summary>
        /// <param name="rangeTester">Tests an item against the custom range.</param>
        /// <param name="node">Node to begin enumeration. May be null.</param>
        /// <returns>An enumerable of the items.</returns>
        /// <exception cref="InvalidOperationException">The tree has an item added or deleted during the enumeration.</exception>
        private IEnumerable<T> EnumerateRangeInOrder(RangeTester rangeTester, Node node)
        {
            int startStamp = changeStamp;

            if (node != null)
            {
                int compare = rangeTester(node.item);

                if (compare >= 0)
                {
                    // At least part of the range may lie to the left.
                    foreach (T item in EnumerateRangeInOrder(rangeTester, node.left))
                    {
                        yield return item;
                        CheckEnumerationStamp(startStamp);
                    }
                }

                if (compare == 0)
                {
                    // The item is within the range.
                    yield return node.item;
                    CheckEnumerationStamp(startStamp);
                }

                if (compare <= 0)
                {
                    // At least part of the range lies to the right.
                    foreach (T item in EnumerateRangeInOrder(rangeTester, node.right))
                    {
                        yield return item;
                        CheckEnumerationStamp(startStamp);
                    }
                }
            }
        }

        /// <summary>
        /// Enumerate the items in a custom range in the tree, in reversed order. The range is determined by 
        /// a RangeTest delegate.
        /// </summary>
        /// <param name="rangeTester">Tests an item against the custom range.</param>
        /// <returns>An IEnumerable&lt;T&gt; that enumerates the custom range in reversed order.</returns>
        /// <exception cref="InvalidOperationException">The tree has an item added or deleted during the enumeration.</exception>
        public IEnumerable<T> EnumerateRangeReversed(RangeTester rangeTester)
        {
            return EnumerateRangeInReversedOrder(rangeTester, root);
        }

        /// <summary>
        /// Enumerate all the items in a custom range, under and including node, in reversed order.
        /// </summary>
        /// <param name="rangeTester">Tests an item against the custom range.</param>
        /// <param name="node">Node to begin enumeration. May be null.</param>
        /// <returns>An enumerable of the items, in reversed oreder.</returns>
        /// <exception cref="InvalidOperationException">The tree has an item added or deleted during the enumeration.</exception>
        private IEnumerable<T> EnumerateRangeInReversedOrder(RangeTester rangeTester, Node node)
        {
            int startStamp = changeStamp;

            if (node != null)
            {
                int compare = rangeTester(node.item);

                if (compare <= 0)
                {
                    // At least part of the range lies to the right.
                    foreach (T item in EnumerateRangeInReversedOrder(rangeTester, node.right))
                    {
                        yield return item;
                        CheckEnumerationStamp(startStamp);
                    }
                }

                if (compare == 0)
                {
                    // The item is within the range.
                    yield return node.item;
                    CheckEnumerationStamp(startStamp);
                }

                if (compare >= 0)
                {
                    // At least part of the range may lie to the left.
                    foreach (T item in EnumerateRangeInReversedOrder(rangeTester, node.left))
                    {
                        yield return item;
                        CheckEnumerationStamp(startStamp);
                    }
                }
            }
        }


        /// <summary>
        /// Deletes either the first or last item from a range, as identified by a RangeTester
        /// delegate. If the range is empty, returns false.
        /// </summary>
        /// <remarks>Top-down algorithm from Weiss. Basic plan is to move down in the tree, 
        /// rotating and recoloring along the way to always keep the current node red, which 
        /// ensures that the node we delete is red. The details are quite complex, however! </remarks>
        /// <param name="rangeTester">Range to delete from.</param>
        /// <param name="deleteFirst">If true, delete the first item from the range, else the last.</param>
        /// <param name="item">Returns the item that was deleted, if true returned.</param>
        /// <returns>True if an element was deleted, false if the range is empty.</returns>
        public bool DeleteItemFromRange(RangeTester rangeTester, bool deleteFirst, out T item)
        {
            Node node;      // The current node.
            Node parent;    // Parent of the current node.
            Node gparent;    // Grandparent of the current node.
            Node sib;      // Sibling of the current node.
            Node keyNode;    // Node with the key that is being removed.

            // The tree may be changed.
            StopEnumerations();

            if (root == null)
            {
                // Nothing in the tree. Go home now.
                item = default(T);
                return false;
            }

            // We decrement counts on the way down the tree. If we end up not finding an item to delete
            // we need a stack to adjust the counts back. 
            Node[] nodeStack = GetNodeStack();
            int nodeStackPtr = 0;  // first free item on the stack.

            // Start at the root.
            node = root;
            sib = parent = gparent = null;
            keyNode = null;

            // Proceed down the tree, making the current node red so it can be removed.
            for (; ; )
            {
                Debug.Assert(parent == null || parent.IsRed);
                Debug.Assert(sib == null || !sib.IsRed);
                Debug.Assert(!node.IsRed);

                if ((node.left == null || !node.left.IsRed) && (node.right == null || !node.right.IsRed))
                {
                    // node has two black children (null children are considered black).
                    if (parent == null)
                    {
                        // Special case for the root.
                        Debug.Assert(node == root);
                        node.IsRed = true;
                    }
                    else if ((sib.left == null || !sib.left.IsRed) && (sib.right == null || !sib.right.IsRed))
                    {
                        // sib has two black children.
                        node.IsRed = true;
                        sib.IsRed = true;
                        parent.IsRed = false;
                    }
                    else
                    {
                        if (parent.left == node && (sib.right == null || !sib.right.IsRed))
                        {
                            // sib has a black child on the opposite side as node.
                            Node tleft = sib.left;
                            Rotate(parent, sib, tleft);
                            sib = tleft;
                        }
                        else if (parent.right == node && (sib.left == null || !sib.left.IsRed))
                        {
                            // sib has a black child on the opposite side as node.
                            Node tright = sib.right;
                            Rotate(parent, sib, tright);
                            sib = tright;
                        }

                        // sib has a red child.
                        Rotate(gparent, parent, sib);
                        node.IsRed = true;
                        sib.IsRed = true;
                        sib.left.IsRed = false;
                        sib.right.IsRed = false;

                        sib.DecrementCount();
                        nodeStack[nodeStackPtr - 1] = sib;
                        parent.DecrementCount();
                        nodeStack[nodeStackPtr++] = parent;
                    }
                }

                // Compare the key and move down the tree to the correct child.
                do
                {
                    Node nextNode, nextSib;    // Node we've moving to, and it's sibling.

                    node.DecrementCount();
                    nodeStack[nodeStackPtr++] = node;

                    // Determine which way to move in the tree by comparing the 
                    // current item to what we're looking for.
                    int compare = rangeTester(node.item);

                    if (compare == 0)
                    {
                        // We've found the node to remove. Remember it, then keep traversing the
                        // tree to either find the first/last of equal keys, and if needed, the predecessor
                        // or successor (the actual node to be removed).
                        keyNode = node;
                        if (deleteFirst)
                        {
                            nextNode = node.left; nextSib = node.right;
                        }
                        else
                        {
                            nextNode = node.right; nextSib = node.left;
                        }
                    }
                    else if (compare > 0)
                    {
                        nextNode = node.left; nextSib = node.right;
                    }
                    else
                    {
                        nextNode = node.right; nextSib = node.left;
                    }

                    // Have we reached the end of our tree walk?
                    if (nextNode == null)
                        goto FINISHED;

                    // Move down the tree.
                    gparent = parent;
                    parent = node;
                    node = nextNode;
                    sib = nextSib;
                } while (!parent.IsRed && node.IsRed);

                if (!parent.IsRed)
                {
                    Debug.Assert(!node.IsRed);
                    // moved to a black child.
                    Rotate(gparent, parent, sib);

                    sib.DecrementCount();
                    nodeStack[nodeStackPtr - 1] = sib;
                    parent.DecrementCount();
                    nodeStack[nodeStackPtr++] = parent;

                    sib.IsRed = false;
                    parent.IsRed = true;
                    gparent = sib;
                    sib = (parent.left == node) ? parent.right : parent.left;
                }
            }

        FINISHED:
            if (keyNode == null)
            {
                // We never found a node to delete.

                // Return counts back to their previous value.
                for (int i = 0; i < nodeStackPtr; ++i)
                    nodeStack[i].IncrementCount();

                // Color the root black, in case it was colored red above.
                if (root != null)
                    root.IsRed = false;

                item = default(T);
                return false;
            }

            // Return the item from the node we're deleting.
            item = keyNode.item;

            // At a leaf or a node with one child which is a leaf. Remove the node.
            if (keyNode != node)
            {
                // The node we want to delete is interior. Move the item from the
                // node we're actually deleting to the key node.
                keyNode.item = node.item;
            }

            // If we have one child, replace the current with the child, otherwise,
            // replace the current node with null.
            Node replacement;
            if (node.left != null)
            {
                replacement = node.left;
                Debug.Assert(!node.IsRed && replacement.IsRed);
                replacement.IsRed = false;
            }
            else if (node.right != null)
            {
                replacement = node.right;
                Debug.Assert(!node.IsRed && replacement.IsRed);
                replacement.IsRed = false;
            }
            else
                replacement = null;

            if (parent == null)
            {
                Debug.Assert(root == node);
                root = replacement;
            }
            else if (parent.left == node)
                parent.left = replacement;
            else
            {
                Debug.Assert(parent.right == node);
                parent.right = replacement;
            }

            // Color the root black, in case it was colored red above.
            if (root != null)
                root.IsRed = false;

            // Update item count.
            count -= 1;

            // And we're done.
            return true;
        }

        /// <summary>
        /// Delete all the items in a range, identified by a RangeTester delegate.
        /// </summary>
        /// <param name="rangeTester">The delegate that defines the range to delete.</param>
        /// <returns>The number of items deleted.</returns>
        public int DeleteRange(RangeTester rangeTester)
        {
            bool deleted;
            int counter = 0;
            T dummy;

            do
            {
                deleted = DeleteItemFromRange(rangeTester, true, out dummy);
                if (deleted)
                    ++counter;
            } while (deleted);

            return counter;
        }

        /// <summary>
        /// Count the items in a custom range in the tree. The range is determined by 
        /// a RangeTester delegate.
        /// </summary>
        /// <param name="rangeTester">The delegate that defines the range.</param>
        /// <returns>The number of items in the range.</returns>
        public int CountRange(RangeTester rangeTester)
        {
            return CountRangeUnderNode(rangeTester, root, false, false);
        }

        /// <summary>
        /// Count all the items in a custom range, under and including node.
        /// </summary>
        /// <param name="rangeTester">The delegate that defines the range.</param>
        /// <param name="node">Node to begin enumeration. May be null.</param>
        /// <param name="belowRangeTop">This node and all under it are either in the range or below it.</param>
        /// <param name="aboveRangeBottom">This node and all under it are either in the range or above it.</param>
        /// <returns>The number of items in the range, under and include node.</returns>
        private int CountRangeUnderNode(RangeTester rangeTester, Node node, bool belowRangeTop, bool aboveRangeBottom)
        {
            if (node != null)
            {
                if (belowRangeTop && aboveRangeBottom)
                {
                    // This node and all below it must be in the range. Use the predefined count.
                    return node.Count;
                }

                int compare = rangeTester(node.item);
                int counter;

                if (compare == 0)
                {
                    counter = 1;  // the node itself
                    counter += CountRangeUnderNode(rangeTester, node.left, true, aboveRangeBottom);
                    counter += CountRangeUnderNode(rangeTester, node.right, belowRangeTop, true);
                }
                else if (compare < 0)
                {
                    counter = CountRangeUnderNode(rangeTester, node.right, belowRangeTop, aboveRangeBottom);
                }
                else
                { // compare > 0
                    counter = CountRangeUnderNode(rangeTester, node.left, belowRangeTop, aboveRangeBottom);
                }

                return counter;
            }
            else
            {
                return 0;
            }
        }

        /// <summary>
        /// Find the first item in a custom range in the tree, and it's index. The range is determined
        /// by a RangeTester delegate.
        /// </summary>
        /// <param name="rangeTester">The delegate that defines the range.</param>
        /// <param name="item">Returns the item found, if true was returned.</param>
        /// <returns>Index of first item in range if range is non-empty, -1 otherwise.</returns>
        public int FirstItemInRange(RangeTester rangeTester, out T item)
        {
            Node node = root, found = null;
            int curCount = 0, foundIndex = -1;

            while (node != null)
            {
                int compare = rangeTester(node.item);

                if (compare == 0)
                {
                    found = node;
                    if (node.left != null)
                        foundIndex = curCount + node.left.Count;
                    else
                        foundIndex = curCount;
                }

                if (compare >= 0)
                    node = node.left;
                else
                {
                    if (node.left != null)
                        curCount += node.left.Count + 1;
                    else
                        curCount += 1;
                    node = node.right;
                }
            }

            if (found != null)
            {
                item = found.item;
                return foundIndex;
            }
            else
            {
                item = default(T);
                return -1;
            }
        }

        /// <summary>
        /// Find the last item in a custom range in the tree, and it's index. The range is determined
        /// by a RangeTester delegate.
        /// </summary>
        /// <param name="rangeTester">The delegate that defines the range.</param>
        /// <param name="item">Returns the item found, if true was returned.</param>
        /// <returns>Index of the item if range is non-empty, -1 otherwise.</returns>
        public int LastItemInRange(RangeTester rangeTester, out T item)
        {
            Node node = root, found = null;
            int curCount = 0, foundIndex = -1;

            while (node != null)
            {
                int compare = rangeTester(node.item);

                if (compare == 0)
                {
                    found = node;
                    if (node.left != null)
                        foundIndex = curCount + node.left.Count;
                    else
                        foundIndex = curCount;
                }

                if (compare <= 0)
                {
                    if (node.left != null)
                        curCount += node.left.Count + 1;
                    else
                        curCount += 1;
                    node = node.right;
                }
                else
                    node = node.left;
            }

            if (found != null)
            {
                item = found.item;
                return foundIndex;
            }
            else
            {
                item = default(T);
                return foundIndex;
            }
        }

        #endregion Ranges

#if DEBUG
        /// <summary>
        /// Prints out the tree.
        /// </summary>
        public void Print()
        {
            PrintSubTree(root, "", "");
            Console.WriteLine();
        }

        /// <summary>
        /// Prints a sub-tree.
        /// </summary>
        /// <param name="node">Node to print from</param>
        /// <param name="prefixNode">Prefix for the node</param>
        /// <param name="prefixChildren">Prefix for the node's children</param>
        private void PrintSubTree(Node node, string prefixNode, string prefixChildren)
        {
            if (node == null)
                return;

            // Red nodes marked as "@@", black nodes as "..".
            Console.WriteLine("{0}{1} {2,4} {3}", prefixNode, node.IsRed ? "@@" : "..", node.Count, node.item);

            PrintSubTree(node.left, prefixChildren + "|-L-", prefixChildren + "|  ");
            PrintSubTree(node.right, prefixChildren + "|-R-", prefixChildren + "   ");
        }

        /// <summary>
        /// Validates that the tree is correctly sorted, and meets the red-black tree 
        /// axioms.
        /// </summary>
        public void Validate()
        {
            Debug.Assert(comparer != null, "Comparer should not be null");

            if (root == null)
            {
                Debug.Assert(0 == count, "Count in empty tree should be 0.");

            }
            else
            {
                Debug.Assert(!root.IsRed, "Root is not black");
                int blackHeight;
                int nodeCount = ValidateSubTree(root, out blackHeight);
                Debug.Assert(nodeCount == this.count, "Node count of tree is not correct.");
            }
        }

        /// <summary>
        /// Validates a sub-tree and returns the count and black height.
        /// </summary>
        /// <param name="node">Sub-tree to validate. May be null.</param>
        /// <param name="blackHeight">Returns the black height of the tree.</param>
        /// <returns>Returns the number of nodes in the sub-tree. 0 if node is null.</returns>
        private int ValidateSubTree(Node node, out int blackHeight)
        {
            if (node == null)
            {
                blackHeight = 0;
                return 0;
            }

            // Check that this node is sorted with respect to any children.
            if (node.left != null)
                Debug.Assert(comparer.Compare(node.left.item, node.item) <= 0, "Left child is not less than or equal to node");
            if (node.right != null)
                Debug.Assert(comparer.Compare(node.right.item, node.item) >= 0, "Right child is not greater than or equal to node");

            // Check that the two-red rule is not violated.
            if (node.IsRed)
            {
                if (node.left != null)
                    Debug.Assert(!node.left.IsRed, "Node and left child both red");
                if (node.right != null)
                    Debug.Assert(!node.right.IsRed, "Node and right child both red");
            }

            // Validate sub-trees and get their size and heights.
            int leftCount, leftBlackHeight;
            int rightCount, rightBlackHeight;
            int ourCount;

            leftCount = ValidateSubTree(node.left, out leftBlackHeight);
            rightCount = ValidateSubTree(node.right, out rightBlackHeight);
            ourCount = leftCount + rightCount + 1;

            Debug.Assert(ourCount == node.Count);

            // Validate the equal black-height rule.
            Debug.Assert(leftBlackHeight == rightBlackHeight, "Black heights are not equal");

            // Calculate our black height and return the count
            blackHeight = leftBlackHeight;
            if (!node.IsRed)
                blackHeight += 1;
            return ourCount;
        }
#endif //DEBUG

    }

}

   
  








Related examples in the same category