/* Copyright 2007-2013 The NGenerics Team (https://github.com/ngenerics/ngenerics/wiki/Team) This program is licensed under the GNU Lesser General Public License (LGPL). You should have received a copy of the license along with the source code. If not, an online copy of the license can be found at http://www.gnu.org/copyleft/lesser.html. */ using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using NGenerics.Comparers; using NGenerics.Patterns.Visitor; using NGenerics.Util; namespace NGenerics.DataStructures.Trees { /// /// A base class for Binary Search Trees that store a single value in each node. /// /// //[Serializable] public abstract class BinarySearchTreeBase : ISearchTree { #region Globals internal const string alreadyContainedInTheTree = "The item is already contained in the tree."; private BinaryTree tree; private readonly IComparer comparer; #endregion #region Delegates /// /// A custom comparison between some search value and the type of item that is kept in the tree. /// /// The type of the search. protected delegate int CustomComparison(TSearch value, T item); #endregion #region Construction /// /// Initializes a new instance of the class. /// protected BinarySearchTreeBase() { comparer = Comparer.Default; } /// /// Initializes a new instance of the class. /// /// The comparer to use when comparing items. /// is a null reference (Nothing in Visual Basic). protected BinarySearchTreeBase(IComparer comparer) { Guard.ArgumentNotNull(comparer, "comparer"); this.comparer = comparer; } /// /// Initializes a new instance of the class. /// /// The comparison. protected BinarySearchTreeBase(Comparison comparison) { Guard.ArgumentNotNull(comparison, "comparison"); comparer = new ComparisonComparer(comparison); } #endregion #region Public Members /// /// Gets the comparer. /// /// The comparer. public IComparer Comparer { get { return comparer; } } #endregion #region Protected Members /// /// Finds the node containing the specified data key. /// /// The item. /// /// The node with the specified key if found. If the key is not in the tree, this method returns null. /// [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")] protected virtual BinaryTree FindNode(T item) { if (tree == null) { return null; } var currentNode = tree; while (currentNode != null) { var nodeResult = comparer.Compare(item, currentNode.Data); if (nodeResult == 0) { return currentNode; } currentNode = nodeResult < 0 ? currentNode.Left : currentNode.Right; } return null; } /// /// Finds the node that matches the custom delegate. /// /// The type of the search. /// The value. /// The custom comparison. /// The item if found, else null. protected virtual BinaryTree FindNode(TSearch value, CustomComparison customComparison) { if (tree == null) { return null; } var currentNode = tree; while (currentNode != null) { var nodeResult = customComparison(value, currentNode.Data); if (nodeResult == 0) { return currentNode; } currentNode = nodeResult < 0 ? currentNode.Left : currentNode.Right; } return null; } /// /// Removes the item from the tree. /// /// The item to remove. /// An indication of whether the item has been removed from the tree. protected abstract bool RemoveItem(T item); /// /// Adds the item. /// /// The item. protected abstract void AddItem(T item); /// /// Find the maximum node. /// /// The maximum node. [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")] protected BinaryTree FindMaximumNode() { #region Debug Debug.Assert(tree != null); #endregion return FindMaximumNode(tree); } /// /// Find the minimum node. /// /// The minimum node. [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")] protected BinaryTree FindMinimumNode() { #region Debug Debug.Assert(tree != null); #endregion return FindMinimumNode(tree); } /// /// Finds the maximum node. /// /// The start node. /// The maximum node below this node. [SuppressMessage("Microsoft.Design", "CA1000:DoNotDeclareStaticMembersOnGenericTypes")] protected static BinaryTree FindMaximumNode(BinaryTree startNode) { #region Asserts Debug.Assert(startNode != null); #endregion var searchNode = startNode; while (searchNode.Right != null) { searchNode = searchNode.Right; } return searchNode; } /// /// Finds the minimum node. /// /// The start node. /// The minimum node below this node. [SuppressMessage("Microsoft.Design", "CA1000:DoNotDeclareStaticMembersOnGenericTypes")] protected static BinaryTree FindMinimumNode(BinaryTree startNode) { #region Asserts Debug.Assert(startNode != null); #endregion var searchNode = startNode; while (searchNode.Left != null) { searchNode = searchNode.Left; } return searchNode; } /// /// Gets or sets the for this . /// [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")] protected BinaryTree Tree { get { return tree; } set { tree = value; } } #endregion #region Private Members /// /// Visits the node in an in-order fashion. /// /// The node. /// The visitor. private static void VisitNode(BinaryTree node, OrderedVisitor visitor) { if (node != null) { var pair = node.Data; visitor.VisitPreOrder(pair); VisitNode(node.Left, visitor); visitor.VisitInOrder(pair); VisitNode(node.Right, visitor); visitor.VisitPostOrder(pair); } } #endregion #region ISearchTree Members /// /// /// /// /// public virtual T Minimum { get { #region Validation ValidateEmpty(); #endregion return FindMinimumNode().Data; } } private void ValidateEmpty() { if (Count == 0) { throw new InvalidOperationException("The search tree is empty."); } } /// /// /// /// /// public virtual T Maximum { get { ValidateEmpty(); return FindMaximumNode().Data; } } /// [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")] public void DepthFirstTraversal(OrderedVisitor visitor) { Guard.ArgumentNotNull(visitor, "visitor"); VisitNode(tree, visitor); } /// [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")] [SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate")] public IEnumerator GetOrderedEnumerator() { if (tree != null) { var trackingVisitor = new TrackingVisitor(); var inOrderVisitor = new InOrderVisitor(trackingVisitor); tree.DepthFirstTraversal(inOrderVisitor); var trackingList = trackingVisitor.TrackingList; for (var i = 0; i < trackingList.Count; i++) { yield return trackingList[i]; } } } /// /// /// /// /// public bool IsEmpty { get { return Count == 0; } } /// public bool Remove(T item) { var itemRemoved = RemoveItem(item); if (itemRemoved) { Count--; } return itemRemoved; } /// /// /// /// /// public void Clear() { ClearItems(); } /// /// Clears all the objects in this instance. /// /// /// Notes to Inheritors: /// Derived classes can override this method to change the behavior of the method. /// protected virtual void ClearItems() { tree = null; Count = 0; } /// /// /// /// /// public int Count { get; private set; } #endregion #region IEnumerable Members /// IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } /// /// Returns an enumerator that iterates through the collection. /// /// /// A that can be used to iterate through the collection. /// /// /// /// /// [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures")] public IEnumerator GetEnumerator() { if (tree != null) { var stack = new Stack>(); stack.Push(tree); while (stack.Count > 0) { var binaryTree = stack.Pop(); yield return binaryTree.Data; if (binaryTree.Left != null) { stack.Push(binaryTree.Left); } if (binaryTree.Right != null) { stack.Push(binaryTree.Right); } } } } #endregion #region ICollection Members /// /// /// /// /// public void Add(T item) { AddItem(item); Count++; } /// /// /// /// /// public virtual bool Contains(T item) { var node = FindNode(item); return node != null; } /// /// /// /// /// public void CopyTo(T[] array, int arrayIndex) { #region Validation Guard.ArgumentNotNull(array, "array"); if ((array.Length - arrayIndex) < Count) { throw new ArgumentException(Constants.NotEnoughSpaceInTheTargetArray, "array"); } #endregion foreach (var association in tree) { array[arrayIndex++] = association; } } /// /// /// /// /// public bool IsReadOnly { get { return false; } } #endregion } }