using System;
using System.Collections.Generic;
using System.Text;
namespace Collections
{
///
/// 2分探索木。
///
/// 要素の型
class BinaryTree : ISet
where T: IComparable
{
#region 内部クラス
///
/// 2文探索木のノード。
///
public class Node
{
#region フィールド
internal T val;
internal Node left, right, parent;
#endregion
#region 初期化
internal Node() : this(default(T), null) { }
internal Node(T val, Node parent)
{
this.val = val;
this.parent = parent;
this.left = this.right = null;
}
#endregion
#region プロパティ
///
/// 格納している要素を取得。
///
public T Value
{
get { return this.val; }
set { this.val = value; }
}
///
/// このノードの次のノードを返す。
/// (要素の値の小さい順にノードをたどる。)
///
public Node Next
{
get
{
Node n = this;
if (n.right != null)
return n.right.Min;
for (; n.parent != null && n.parent.left != n; n = n.parent) ;
return n.parent;
}
}
///
/// このノードの前のノードを返す。
/// (要素の値の小さい順にノードをたどる。)
///
public Node Previous
{
get
{
Node n = this;
if (n.left != null)
return n.left.Max;
for (; n.parent != null && n.parent.right != n; n = n.parent) ;
return n.parent;
}
}
///
/// このノード以下の部分木中で、最小の要素を持つノード(=左端ノード)を返す。
///
internal Node Min
{
get
{
Node n = this;
for (; n.left != null; n = n.left) ;
return n;
}
}
///
/// このノード以下の部分木中で、最大の要素を持つノード(=右端ノード)を返す。
///
internal Node Max
{
get
{
Node n = this;
for (; n.right != null; n = n.right) ;
return n;
}
}
#endregion
#region デバッグ用
[System.Diagnostics.Conditional("DEBUG")]
virtual public void Output(System.IO.TextWriter writer, int level)
{
for (int i = 0; i < level; ++i)
writer.Write("\t");
writer.Write("{0}\n", this.val);
++level;
if (this.left != null) this.left.Output(writer, level);
else
{
for (int i = 0; i < level; ++i)
writer.Write("\t");
writer.Write("null\n");
}
if (this.right != null) this.right.Output(writer, level);
else
{
for (int i = 0; i < level; ++i)
writer.Write("\t");
writer.Write("null\n");
}
}
#endregion
}
#endregion
#region フィールド
///
/// 根ノード。
///
Node root;
#endregion
#region 初期化
public BinaryTree()
{
this.root = null;
}
#endregion
#region プロパティ
///
/// 木構造を逐次探索する際の始点。
///
public Node Begin
{
get
{
if (this.root == null)
return this.End;
return this.root.Min;
}
}
///
/// 木構造を逐次探索する際の終端(末尾よりも後ろの番兵に当たるノード)。
///
public Node End
{
get { return null; }
}
#endregion
#region 要素の挿入・削除・検索
///
/// 新しい要素を挿入する。
///
/// 新しい要素
/// 新しい要素を格納するノード
public void Insert(T elem)
{
if (this.root == null)
{
this.root = new Node(elem, null);
return;
}
Node n = this.root;
Node p = null;
while (n != null)
{
p = n;
if (n.val.CompareTo(elem) > 0) n = n.left;
else n = n.right;
}
n = new Node(elem, p);
if (p.val.CompareTo(elem) > 0) p.left = n;
else p.right = n;
}
///
/// n の片方の子は null、もう片方の子は m という前提の元で、
/// ノード n の位置を子ノード m で置き換える。
///
/// 削除するノード
/// 置き換える子ノード
void Replace(Node n, Node m)
{
Node p = n.parent;
if (m != null) m.parent = p;
if (n == this.root) this.root = m;
else if (p.left == n) p.left = m;
else p.right = m;
}
///
/// ノード n を削除する。
///
/// 削除したいノード
public void Erase(Node n)
{
if (n == null) return;
if (n.left == null) this.Replace(n, n.right);
else if(n.right == null) this.Replace(n, n.left);
else
{
Node m = n.right.Min;
n.Value = m.Value;
this.Replace(m, m.right);
}
}
///
/// 要素を削除する。
///
/// 削除したい要素
public void Erase(T elem)
{
this.Erase(this.Find(elem));
}
///
/// ある値を持つノードを検索。
/// (同じ値が複数ある場合、最初のノード。)
/// 見つからなかった場合は null を返す。
///
/// 探したい要素
/// 見つけたノード
public Node Find(T elem)
{
Node n = this.root;
while (n != null)
{
if (n.val.CompareTo(elem) > 0) n = n.left;
else if (n.val.CompareTo(elem) < 0) n = n.right;
else break;
}
return n;
}
public bool Contains(T elem)
{
return this.Find(elem) != this.End;
}
///
/// ある値のノードを検索。
/// (同じ値を持つノード全部を IEnumerable で一覧として返す。)
///
/// 探したい要素
/// 見つけたノード一覧
public IEnumerable FindRange(T elem)
{
for (Node n = this.Find(elem); n != this.End && n.Value.CompareTo(elem) == 0; n = n.Next)
yield return n.Value;
}
#endregion
#region IEnumerable メンバ
public IEnumerator GetEnumerator()
{
for (Node n = this.Begin; n != this.End; n = n.Next)
yield return n.Value;
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
#endregion
#region デバッグ用
[System.Diagnostics.Conditional("DEBUG")]
public void Output(System.IO.TextWriter writer)
{
if (this.root != null)
this.root.Output(writer, 0);
}
#endregion
}
}