using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
namespace Advanced.Algorithms.DataStructures
{
///
/// A ternary search tree implementation.
///
public class TernarySearchTree : IEnumerable where T : IComparable
{
private TernarySearchTreeNode root;
public int Count { get; private set; }
public TernarySearchTree()
{
Count = 0;
}
///
/// Time complexity: O(m) where m is the length of entry.
///
public void Insert(T[] entry)
{
insert(ref root, null, entry, 0);
Count++;
}
///
/// Insert a new record to this ternary search tree after finding the end recursively.
///
private void insert(ref TernarySearchTreeNode currentNode,
TernarySearchTreeNode parent,
T[] entry, int currentIndex)
{
//create new node if empty
if (currentNode == null)
{
currentNode = new TernarySearchTreeNode(parent, entry[currentIndex]);
}
var compareResult = currentNode.Value.CompareTo(entry[currentIndex]);
//current is greater? move left, move right otherwise
//if current is equal then move center
if (compareResult > 0)
{
//move left
var left = currentNode.Left;
insert(ref left, parent, entry, currentIndex);
currentNode.Left = left;
}
else if (compareResult < 0)
{
//move right
var right = currentNode.Right;
insert(ref right, parent, entry, currentIndex);
currentNode.Right = right;
}
else
{
if (currentIndex != entry.Length - 1)
{
//if equal we just skip to next element
var middle = currentNode.Middle;
insert(ref middle, currentNode, entry, currentIndex + 1);
currentNode.Middle = middle;
}
//end of word
else
{
if (currentNode.IsEnd)
{
throw new Exception("Item exists.");
}
currentNode.IsEnd = true;
return;
}
}
}
///
/// Deletes a record from this ternary search tree.
/// Time complexity: O(m) where m is the length of entry.
///
public void Delete(T[] entry)
{
delete(root, entry, 0);
Count--;
}
///
/// Deletes a record from this TernarySearchTree after finding it recursively.
///
private void delete(TernarySearchTreeNode currentNode,
T[] entry, int currentIndex)
{
//empty node
if (currentNode == null)
{
throw new Exception("Item not found.");
}
var compareResult = currentNode.Value.CompareTo(entry[currentIndex]);
TernarySearchTreeNode child;
//current is greater? move left, move right otherwise
//if current is equal then move center
if (compareResult > 0)
{
//move left
child = currentNode.Left;
delete(child, entry, currentIndex);
//delete if middle is not end
//and we if have'nt deleted the node yet
if (child.HasChildren == false
&& !child.IsEnd)
{
currentNode.Left = null;
}
}
else if (compareResult < 0)
{
//move right
child = currentNode.Right;
delete(child, entry, currentIndex);
//delete if middle is not end
//and we if have'nt deleted the node yet
if (child.HasChildren == false
&& !child.IsEnd)
{
currentNode.Right = null;
}
}
else
{
if (currentIndex != entry.Length - 1)
{
//if equal we just skip to next element
child = currentNode.Middle;
delete(child, entry, currentIndex + 1);
//delete if middle is not end
//and we if have'nt deleted the node yet
if (child.HasChildren == false
&& !child.IsEnd)
{
currentNode.Middle = null;
}
}
//end of word
else
{
if (!currentNode.IsEnd)
{
throw new Exception("Item not found.");
}
//remove this end flag
currentNode.IsEnd = false;
return;
}
}
}
///
/// Returns a list of records matching this prefix.
/// Time complexity: O(rm) where r is the number of results and m is the average length of each entry.
///
public List StartsWith(T[] prefix)
{
return startsWith(root, prefix, 0);
}
///
/// Recursively visit until end of prefix and then gather all suffixes under it.
///
private List startsWith(TernarySearchTreeNode currentNode, T[] searchPrefix, int currentIndex)
{
while (true)
{
if (currentNode == null)
{
return new List();
}
var compareResult = currentNode.Value.CompareTo(searchPrefix[currentIndex]);
//current is greater? move left, move right otherwise
//if current is equal then move center
if (compareResult > 0)
{
//move left
currentNode = currentNode.Left;
continue;
}
if (compareResult < 0)
{
//move right
currentNode = currentNode.Right;
continue;
}
//end of search Prefix, so gather all words under it
if (currentIndex != searchPrefix.Length - 1)
{
currentNode = currentNode.Middle;
currentIndex = currentIndex + 1;
continue;
}
var result = new List();
gatherStartsWith(result, searchPrefix.ToList(), currentNode.Middle);
return result;
}
}
///
/// Gathers all suffixes under this node appending with the given prefix
///
private void gatherStartsWith(List result, List prefix, TernarySearchTreeNode node)
{
while (true)
{
if (node == null)
{
result.Add(prefix.ToArray());
return;
}
//end of word
if (node.IsEnd)
{
//append to end of prefix for new prefix
result.Add(prefix.Concat(new[] { node.Value }).ToArray());
}
if (node.Left != null)
{
gatherStartsWith(result, prefix, node.Left);
}
if (node.Middle != null)
{
//append to end of prefix for new prefix
prefix.Add(node.Value);
gatherStartsWith(result, prefix, node.Middle);
prefix.RemoveAt(prefix.Count - 1);
}
if (node.Right != null)
{
node = node.Right;
continue;
}
break;
}
}
///
/// Returns true if the entry exist.
/// Time complexity: O(e) where e is the length of the given entry.
///
public bool Contains(T[] entry)
{
return search(root, entry, 0, false);
}
///
/// Returns true if the entry prefix exist.
/// Time complexity: O(e) where e is the length of the given entry.
///
public bool ContainsPrefix(T[] entry)
{
return search(root, entry, 0, true);
}
///
/// Find if the record exist recursively.
///
private bool search(TernarySearchTreeNode currentNode, T[] searchEntry, int currentIndex, bool isPrefixSearch)
{
while (true)
{
//create new node if empty
if (currentNode == null)
{
return false;
}
//end of word, so return
if (currentIndex == searchEntry.Length - 1)
{
return isPrefixSearch || currentNode.IsEnd;
}
var compareResult = currentNode.Value.CompareTo(searchEntry[currentIndex]);
//current is greater? move left, move right otherwise
//if current is equal then move center
if (compareResult > 0)
{
//move left
currentNode = currentNode.Left;
continue;
}
if (compareResult < 0)
{
//move right
currentNode = currentNode.Right;
continue;
}
//if equal we just skip to next element
currentNode = currentNode.Middle;
currentIndex = currentIndex + 1;
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public IEnumerator GetEnumerator()
{
return new TernarySearchTreeEnumerator(root);
}
}
internal class TernarySearchTreeNode where T : IComparable
{
internal bool IsEnd { get; set; }
internal T Value { get; set; }
internal bool HasChildren => !(Left == null && Middle == null && Right == null);
internal TernarySearchTreeNode Parent { get; set; }
internal TernarySearchTreeNode Left { get; set; }
internal TernarySearchTreeNode Middle { get; set; }
internal TernarySearchTreeNode Right { get; set; }
internal TernarySearchTreeNode(TernarySearchTreeNode parent, T value)
{
Parent = parent;
this.Value = value;
}
}
internal class TernarySearchTreeEnumerator : IEnumerator where T : IComparable
{
private readonly TernarySearchTreeNode root;
private Stack> progress;
internal TernarySearchTreeEnumerator(TernarySearchTreeNode root)
{
this.root = root;
}
public bool MoveNext()
{
if (root == null)
{
return false;
}
if (progress == null)
{
progress = new Stack>(new[] { root });
}
while (progress.Count > 0)
{
var next = progress.Pop();
foreach (var child in new[] { next.Left, next.Middle, next.Right }.Where(x => x != null))
{
progress.Push(child);
}
if (next.IsEnd)
{
Current = getValue(next);
return true;
}
}
return false;
}
private T[] getValue(TernarySearchTreeNode next)
{
var result = new Stack();
result.Push(next.Value);
while (next.Parent != null && !next.Parent.Value.Equals(default(T)))
{
next = next.Parent;
result.Push(next.Value);
}
return result.ToArray();
}
public void Reset()
{
progress = null;
Current = null;
}
public T[] Current { get; private set; }
object IEnumerator.Current => Current;
public void Dispose()
{
progress = null;
}
}
}