/*
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
}
}