using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
namespace Advanced.Algorithms.DataStructures
{
///
/// A trie (prefix tree) implementation.
///
public class Trie : IEnumerable
{
private TrieNode root { get; set; }
public int Count { get; private set; }
public Trie()
{
root = new TrieNode(null, default(T));
Count = 0;
}
///
/// Insert a new record to this trie.
/// Time complexity: O(m) where m is the length of entry.
///
public void Insert(T[] entry)
{
insert(root, entry, 0);
Count++;
}
///
/// Insert a new record to this trie after finding the end recursively.
///
private void insert(TrieNode currentNode, T[] entry, int currentIndex)
{
while (true)
{
if (currentIndex == entry.Length)
{
currentNode.IsEnd = true;
return;
}
if (currentNode.Children.ContainsKey(entry[currentIndex]) == false)
{
var newNode = new TrieNode(currentNode, entry[currentIndex]);
currentNode.Children.Add(entry[currentIndex], newNode);
currentNode = newNode;
currentIndex = currentIndex + 1;
}
else
{
currentNode = currentNode.Children[entry[currentIndex]];
currentIndex = currentIndex + 1;
}
}
}
///
/// Deletes a record from this trie.
/// 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 trie after finding it recursively.
///
private void delete(TrieNode currentNode, T[] entry, int currentIndex)
{
if (currentIndex == entry.Length)
{
if (!currentNode.IsEnd)
{
throw new Exception("Item not in trie.");
}
currentNode.IsEnd = false;
return;
}
if (currentNode.Children.ContainsKey(entry[currentIndex]) == false)
{
throw new Exception("Item not in trie.");
}
delete(currentNode.Children[entry[currentIndex]], entry, currentIndex + 1);
if (currentNode.Children[entry[currentIndex]].IsEmpty
&& !currentNode.IsEnd)
{
currentNode.Children.Remove(entry[currentIndex]);
}
}
///
/// 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 sub entries under it.
///
private List startsWith(TrieNode currentNode, T[] searchPrefix, int currentIndex)
{
while (true)
{
if (currentIndex == searchPrefix.Length)
{
var result = new List();
//gather sub entries and prefix them with search entry prefix
gatherStartsWith(result, searchPrefix, new List(), currentNode);
return result;
}
if (currentNode.Children.ContainsKey(searchPrefix[currentIndex]) == false)
{
return new List();
}
currentNode = currentNode.Children[searchPrefix[currentIndex]];
currentIndex = currentIndex + 1;
}
}
///
/// Gathers all suffixes under this node appending with the given prefix.
///
private void gatherStartsWith(List result, T[] searchPrefix, List suffix,
TrieNode node)
{
//end of word
if (node.IsEnd)
{
if (suffix != null)
{
result.Add(searchPrefix.Concat(suffix).ToArray());
}
else
{
result.Add(searchPrefix);
}
}
//visit all children
foreach (var child in node.Children)
{
//append to end of prefix for new prefix
suffix.Add(child.Key);
gatherStartsWith(result, searchPrefix, suffix, child.Value);
suffix.RemoveAt(suffix.Count - 1);
}
}
///
/// 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 contains(root, entry, 0, false);
}
///
/// Returns true if any records match this prefix.
/// Time complexity: O(e) where e is the length of the given entry.
///
public bool ContainsPrefix(T[] prefix)
{
return contains(root, prefix, 0, true);
}
///
/// Find if the record exist recursively.
///
private bool contains(TrieNode currentNode, T[] entry, int currentIndex, bool isPrefixSearch)
{
while (true)
{
if (currentIndex == entry.Length)
{
return isPrefixSearch || currentNode.IsEnd;
}
if (currentNode.Children.ContainsKey(entry[currentIndex]) == false)
{
return false;
}
currentNode = currentNode.Children[entry[currentIndex]];
currentIndex = currentIndex + 1;
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public IEnumerator GetEnumerator()
{
return new TrieEnumerator(root);
}
}
internal class TrieNode
{
internal bool IsEmpty => Children.Count == 0;
internal bool IsEnd { get; set; }
internal TrieNode Parent { get; set; }
internal Dictionary> Children { get; set; }
internal T Value { get; set; }
internal TrieNode(TrieNode parent, T value)
{
Parent = parent;
Value = value;
Children = new Dictionary>();
}
}
internal class TrieEnumerator : IEnumerator
{
private readonly TrieNode root;
private Stack> progress;
internal TrieEnumerator(TrieNode root)
{
this.root = root;
}
public bool MoveNext()
{
if (root == null)
{
return false;
}
if (progress == null)
{
progress = new Stack>(root.Children.Select(x => x.Value));
}
while (progress.Count > 0)
{
var next = progress.Pop();
foreach (var child in next.Children)
{
progress.Push(child.Value);
}
if (next.IsEnd)
{
Current = getValue(next);
return true;
}
}
return false;
}
private T[] getValue(TrieNode 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;
}
}
}