using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
namespace Advanced.Algorithms.DataStructures
{
///
/// A binary tree implementation using pointers.
///
public class BinaryTree : IEnumerable where T : IComparable
{
private BinaryTreeNode root { get; set; }
public int Count { get; private set; }
///
/// Time complexity: O(n)
///
public bool HasItem(T value)
{
if (root == null)
{
return false;
}
return find(root, value) != null;
}
///
/// Time complexity: O(n)
///
public int GetHeight()
{
return getHeight(root);
}
///
/// Only inserts to unambiguous nodes (a node with two children cannot be inserted with a new child unambiguously).
/// Time complexity: O(n)
///
///
public void Insert(T parent, T child)
{
if (root == null)
{
root = new BinaryTreeNode(null, child);
Count++;
return;
}
var parentNode = find(parent);
if (parentNode == null)
{
throw new Exception("Cannot find parent node");
}
var exists = find(root, child) != null;
if (exists)
{
throw new ArgumentNullException("value already exists");
}
switch (parentNode.Left)
{
case null when parentNode.Right == null:
parentNode.Left = new BinaryTreeNode(parentNode, child);
break;
case null:
parentNode.Left = new BinaryTreeNode(parentNode, child);
break;
default:
if (parentNode.Right == null)
{
parentNode.Right = new BinaryTreeNode(parentNode, child);
}
else
{
throw new Exception("Cannot insert to a parent with two child node unambiguosly");
}
break;
}
Count++;
}
///
/// Only deletes unambiguous nodes (a node with two children cannot be deleted unambiguously).
/// Time complexity: O(n)
///
public void Delete(T value)
{
var node = find(value);
if (node == null)
{
throw new Exception("Cannot find node");
}
switch (node.Left)
{
case null when node.Right == null:
if (node.Parent == null)
{
root = null;
}
else
{
if (node.Parent.Left == node)
{
node.Parent.Left = null;
}
else
{
node.Parent.Right = null;
}
}
break;
case null when node.Right != null:
node.Right.Parent = node.Parent;
if (node.Parent.Left == node)
{
node.Parent.Left = node.Right;
}
else
{
node.Parent.Right = node.Right;
}
break;
default:
if (node.Right == null && node.Left != null)
{
node.Left.Parent = node.Parent;
if (node.Parent.Left == node)
{
node.Parent.Left = node.Left;
}
else
{
node.Parent.Right = node.Left;
}
}
else
{
throw new Exception("Cannot delete two child node unambiguosly");
}
break;
}
Count--;
}
///
/// Time complexity: O(n)
///
public IEnumerable Children(T value)
{
var node = find(value);
if (node != null)
{
return new[] { node.Left, node.Right }.Where(x => x != null).Select(x => x.Value);
}
return null;
}
private int getHeight(BinaryTreeNode node)
{
if (node == null)
{
return -1;
}
return Math.Max(getHeight(node.Left), getHeight(node.Right)) + 1;
}
private BinaryTreeNode find(T value)
{
return root == null ? null : find(root, value);
}
private BinaryTreeNode find(BinaryTreeNode parent, T value)
{
while (true)
{
if (parent == null)
{
return null;
}
if (parent.Value.CompareTo(value) == 0)
{
return parent;
}
var left = find(parent.Left, value);
if (left != null)
{
return left;
}
parent = parent.Right;
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public IEnumerator GetEnumerator()
{
return new BinaryTreeEnumerator(root);
}
}
internal class BinaryTreeNode : IComparable where T : IComparable
{
internal T Value { get; set; }
internal BinaryTreeNode Parent { get; set; }
internal BinaryTreeNode Left { get; set; }
internal BinaryTreeNode Right { get; set; }
internal bool IsLeaf => Left == null && Right == null;
internal BinaryTreeNode(BinaryTreeNode parent, T value)
{
Parent = parent;
Value = value;
}
public int CompareTo(object obj)
{
return Value.CompareTo(obj as BinaryTreeNode);
}
}
internal class BinaryTreeEnumerator : IEnumerator where T : IComparable
{
private readonly BinaryTreeNode root;
private Stack> progress;
internal BinaryTreeEnumerator(BinaryTreeNode root)
{
this.root = root;
}
public bool MoveNext()
{
if (root == null)
{
return false;
}
if (progress == null)
{
progress = new Stack>(new[] { root.Left, root.Right }.Where(x => x != null));
Current = root.Value;
return true;
}
if (progress.Count > 0)
{
var next = progress.Pop();
Current = next.Value;
foreach (var node in new[] { next.Left, next.Right }.Where(x => x != null))
{
progress.Push(node);
}
return true;
}
return false;
}
public void Reset()
{
progress = null;
Current = default(T);
}
public T Current { get; private set; }
object IEnumerator.Current => Current;
public void Dispose()
{
progress = null;
}
}
}