/*
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 NGenerics.Patterns.Visitor;
using NGenerics.Util;
using System.Diagnostics.CodeAnalysis;
namespace NGenerics.DataStructures.Trees
{
///
/// An implementation of a Binary Tree data structure.
///
/// The type of elements in the .
//[Serializable]
[SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")]
public class BinaryTree : ICollection, ITree
{
#region Globals
private BinaryTree leftSubtree;
private BinaryTree rightSubtree;
private T data;
#endregion
#region Construction
/// The data contained in this node.
public BinaryTree(T data) : this(data, null, null) { }
/// The data.
/// The data of the left subtree.
/// The data of the right subtree.
public BinaryTree(T data, T left, T right) : this(data, new BinaryTree(left), new BinaryTree(right)) { }
/// The data contained in this node.
/// The left subtree.
/// The right subtree.
public BinaryTree(T data, BinaryTree left, BinaryTree right)
: this(data, left, right, true)
{
}
/// The data contained in this node.
/// The left subtree.
/// The right subtree.
/// to validate ; otherwise .
internal BinaryTree(T data, BinaryTree left, BinaryTree right, bool validateData)
{
#region Validation
//TODO: probably not the most efficient way of doing this but SplayTree needs to use a BinaryTree with null data.
if (validateData)
{
Guard.ArgumentNotNull(data, "data");
}
#endregion
leftSubtree = left;
if (left != null)
{
left.Parent = this;
}
rightSubtree = right;
if (right != null)
{
right.Parent = this;
}
this.data = data;
}
#endregion
#region ICollection Members
///
public bool IsEmpty
{
get
{
return Count == 0;
}
}
///
public bool IsFull
{
get
{
return (leftSubtree != null) && (rightSubtree != null);
}
}
///
public bool Contains(T item)
{
foreach (var thisItem in this)
{
if (item.Equals(thisItem))
{
return true;
}
}
return false;
}
///
public void CopyTo(T[] array, int arrayIndex)
{
Guard.ArgumentNotNull(array, "array");
foreach (var item in this)
{
#region Validation
if (arrayIndex >= array.Length)
{
throw new ArgumentException(Constants.NotEnoughSpaceInTheTargetArray, "array");
}
#endregion
array[arrayIndex++] = item;
}
}
///
public int Count
{
get
{
var count = 0;
if (leftSubtree != null)
{
count++;
}
if (rightSubtree != null)
{
count++;
}
return count;
}
}
///
public void Add(T item)
{
AddItem(new BinaryTree(item));
}
///
public bool Remove(T item)
{
if (leftSubtree != null)
{
if (leftSubtree.data.Equals(item))
{
RemoveLeft();
return true;
}
}
if (rightSubtree != null)
{
if (rightSubtree.data.Equals(item))
{
RemoveRight();
return true;
}
}
return false;
}
///
/// Removes the specified child.
///
/// The child.
/// A value indicating whether the child was found (and removed) from this tree.
public bool Remove(BinaryTree child)
{
if (leftSubtree != null)
{
if (leftSubtree == child)
{
RemoveLeft();
return true;
}
}
if (rightSubtree != null)
{
if (rightSubtree == child)
{
RemoveRight();
return true;
}
}
return false;
}
///
/// Returns an enumerator that iterates through the collection.
///
///
/// A that can be used to iterate through the collection.
///
public IEnumerator GetEnumerator()
{
var stack = new Stack>();
stack.Push(this);
while (stack.Count > 0)
{
var tree = stack.Pop();
yield return tree.Data;
if (tree.leftSubtree != null)
{
stack.Push(tree.leftSubtree);
}
if (tree.rightSubtree != null)
{
stack.Push(tree.rightSubtree);
}
}
}
///
public virtual void Clear()
{
if (leftSubtree != null)
{
leftSubtree.Parent = null;
leftSubtree = null;
}
if (rightSubtree != null)
{
rightSubtree.Parent = null;
rightSubtree = null;
}
}
#endregion
#region ITree Members
///
void ITree.Add(ITree child)
{
AddItem((BinaryTree)child);
}
///
ITree ITree.GetChild(int index)
{
return GetChild(index);
}
///
bool ITree.Remove(ITree child)
{
return Remove((BinaryTree)child);
}
///
ITree ITree.FindNode(Predicate condition)
{
return FindNode(condition);
}
///
ITree ITree.Parent
{
get
{
return Parent;
}
}
#endregion
#region Public Members
///
/// Gets the parent of the current node..
///
/// The parent of the current node.
public BinaryTree Parent { get; private set; }
///
/// Finds the node with the specified condition. If a node is not found matching
/// the specified condition, null is returned.
///
/// The condition to test.
/// The first node that matches the condition supplied. If a node is not found, null is returned.
/// is a null reference (Nothing in Visual Basic).
public BinaryTree FindNode(Predicate condition)
{
Guard.ArgumentNotNull(condition, "condition");
if (condition(Data))
{
return this;
}
if (leftSubtree != null)
{
var ret = leftSubtree.FindNode(condition);
if (ret != null)
{
return ret;
}
}
if (rightSubtree != null)
{
var ret = rightSubtree.FindNode(condition);
if (ret != null)
{
return ret;
}
}
return null;
}
///
/// Gets or sets the left subtree.
///
/// The left subtree.
[SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual BinaryTree Left
{
get
{
return leftSubtree;
}
set
{
if (leftSubtree != null)
{
RemoveLeft();
}
if (value != null)
{
if (value.Parent != null)
{
value.Parent.Remove(value);
}
value.Parent = this;
}
leftSubtree = value;
}
}
///
/// Gets or sets the right subtree.
///
/// The right subtree.
[SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")]
public virtual BinaryTree Right
{
get
{
return rightSubtree;
}
set
{
if (rightSubtree != null)
{
RemoveRight();
}
if (value != null)
{
if (value.Parent != null)
{
value.Parent.Remove(value);
}
value.Parent = this;
}
rightSubtree = value;
}
}
///
public virtual T Data
{
get
{
return data;
}
set
{
#region Validation
Guard.ArgumentNotNull(value, "data");
#endregion
data = value;
}
}
///
public int Degree
{
get
{
return Count;
}
}
///
/// Gets the child at the specified index.
///
/// The index of the child in question.
/// The child at the specified index.
/// does not equal 0 or 1.
public BinaryTree GetChild(int index)
{
switch (index)
{
case 0:
return leftSubtree;
case 1:
return rightSubtree;
default:
throw new ArgumentOutOfRangeException("index");
}
}
///
public virtual int Height
{
get
{
if (Degree == 0)
{
return 0;
}
return 1 + FindMaximumChildHeight();
}
}
///
/// Performs a depth first traversal on this tree with the specified visitor.
///
/// The ordered visitor.
/// is a null reference (Nothing in Visual Basic).
public virtual void DepthFirstTraversal(OrderedVisitor orderedVisitor)
{
Guard.ArgumentNotNull(orderedVisitor, "orderedVisitor");
if (orderedVisitor.HasCompleted)
{
return;
}
// Preorder visit
orderedVisitor.VisitPreOrder(Data);
if (leftSubtree != null)
{
leftSubtree.DepthFirstTraversal(orderedVisitor);
}
// In-order visit
orderedVisitor.VisitInOrder(data);
if (rightSubtree != null)
{
rightSubtree.DepthFirstTraversal(orderedVisitor);
}
// PostOrder visit
orderedVisitor.VisitPostOrder(Data);
}
///
/// Performs a breadth first traversal on this tree with the specified visitor.
///
/// The visitor.
/// is a null reference (Nothing in Visual Basic).
public virtual void BreadthFirstTraversal(IVisitor visitor)
{
Guard.ArgumentNotNull(visitor, "visitor");
var queue = new Queue>();
queue.Enqueue(this);
while (queue.Count > 0)
{
if (visitor.HasCompleted)
{
break;
}
var binaryTree = queue.Dequeue();
visitor.Visit(binaryTree.Data);
for (var i = 0; i < binaryTree.Degree; i++)
{
var child = binaryTree.GetChild(i);
if (child != null)
{
queue.Enqueue(child);
}
}
}
}
///
public virtual bool IsLeafNode
{
get
{
return Degree == 0;
}
}
///
/// Removes the left child.
///
public virtual void RemoveLeft()
{
if (leftSubtree != null)
{
leftSubtree.Parent = null;
leftSubtree = null;
}
}
///
/// Removes the left child.
///
public virtual void RemoveRight()
{
if (rightSubtree != null)
{
rightSubtree.Parent = null;
rightSubtree = null;
}
}
///
/// Adds an item to the .
///
/// The subtree.
/// The is read-only.
/// The is full.
/// is null (Nothing in Visual Basic).
public void Add(BinaryTree subtree)
{
Guard.ArgumentNotNull(subtree, "subtree");
AddItem(subtree);
}
///
/// Adds an item to the .
///
/// The subtree.
///
/// Notes to Inheritors:
/// Derived classes can override this method to change the behavior of the method.
///
protected virtual void AddItem(BinaryTree subtree)
{
if (leftSubtree == null)
{
if (subtree.Parent != null)
{
subtree.Parent.Remove(subtree);
}
leftSubtree = subtree;
subtree.Parent = this;
}
else if (rightSubtree == null)
{
if (subtree.Parent != null)
{
subtree.Parent.Remove(subtree);
}
rightSubtree = subtree;
subtree.Parent = this;
}
else
{
throw new InvalidOperationException("This binary tree is full.");
}
}
#endregion
#region Private Members
///
/// Finds the maximum height between the child nodes.
///
/// The maximum height of the tree between all paths from this node and all leaf nodes.
protected virtual int FindMaximumChildHeight()
{
var leftHeight = 0;
var rightHeight = 0;
if (leftSubtree != null)
{
leftHeight = leftSubtree.Height;
}
if (rightSubtree != null)
{
rightHeight = rightSubtree.Height;
}
return leftHeight > rightHeight ? leftHeight : rightHeight;
}
#endregion
#region Operator Overloads
///
/// Gets the at the specified index.
///
public BinaryTree this[int index]
{
get
{
return GetChild(index);
}
}
#endregion
#region ICollection Members
///
public bool IsReadOnly
{
get
{
return false;
}
}
#endregion
#region IEnumerable Members
///
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
#endregion
#region Object Members
///
public override string ToString()
{
return data.ToString();
}
#endregion
}
}