790 lines
22 KiB
C#
790 lines
22 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
namespace ln.collections
|
|
{
|
|
public class BTree<K,V> : IDict<K,V>, IEnumerable<KeyValuePair<K,V>>
|
|
{
|
|
public Comparison<K> Comparison { get; }
|
|
public int Count => count;
|
|
public bool Empty => count == 0;
|
|
|
|
public int LeftDepth => headNode != null ? headNode.LeftDepth : 0;
|
|
public int RightDepth => headNode != null ? headNode.RightDepth : 0;
|
|
public int Depth => Math.Max(LeftDepth, RightDepth);
|
|
|
|
public int LimBalance
|
|
{
|
|
get => limBalance;
|
|
set
|
|
{
|
|
if (value == 0)
|
|
throw new ArgumentException(nameof(value));
|
|
limBalance = value;
|
|
}
|
|
}
|
|
|
|
TreeNode headNode;
|
|
private int count;
|
|
private int limBalance;
|
|
|
|
public BTree()
|
|
{
|
|
if (!typeof(K).GetInterfaces().Contains(typeof(IComparable<K>)))
|
|
{
|
|
Comparison = (K x, K y) => x.GetHashCode() - y.GetHashCode();
|
|
}
|
|
else
|
|
{
|
|
Comparison = (K x, K y) => ((IComparable<K>)x).CompareTo(y);
|
|
}
|
|
}
|
|
public BTree(Comparison<K> comparison)
|
|
{
|
|
Comparison = comparison;
|
|
}
|
|
|
|
public V this[K key]
|
|
{
|
|
get
|
|
{
|
|
TreeNode node = Find(key);
|
|
if (object.ReferenceEquals(node,null))
|
|
throw new KeyNotFoundException();
|
|
return node.Value;
|
|
}
|
|
set
|
|
{
|
|
TreeNode node = Find(key);
|
|
if (object.ReferenceEquals(node, null))
|
|
{
|
|
Add(key, value);
|
|
}
|
|
else
|
|
{
|
|
node.Value = value;
|
|
}
|
|
}
|
|
}
|
|
public Boolean TryGet(K key, out V value)
|
|
{
|
|
TreeNode node = Find(key);
|
|
if (object.ReferenceEquals(node, null))
|
|
{
|
|
value = default(V);
|
|
return false;
|
|
}
|
|
|
|
value = node.Value;
|
|
return true;
|
|
}
|
|
|
|
public virtual void Add(K key,V value)
|
|
{
|
|
TreeNode treeNode = new TreeNode(this, key, value);
|
|
Insert(treeNode);
|
|
count++;
|
|
}
|
|
public virtual void TryAdd(K key,V value)
|
|
{
|
|
TreeNode treeNode = Find(key);
|
|
if (object.ReferenceEquals(treeNode, null))
|
|
Add(key, value);
|
|
}
|
|
|
|
public virtual void Remove(K key)
|
|
{
|
|
TreeNode node = Find(key);
|
|
if (object.ReferenceEquals(node, null))
|
|
throw new KeyNotFoundException();
|
|
|
|
Remove(node);
|
|
count--;
|
|
}
|
|
public virtual bool TryRemove(K key)
|
|
{
|
|
TreeNode node = Find(key);
|
|
if (node != null)
|
|
Remove(node);
|
|
|
|
return node != null;
|
|
}
|
|
|
|
public bool ContainsKey(K key)
|
|
{
|
|
return Find(key) != null;
|
|
}
|
|
public bool ContainsValue(V value)
|
|
{
|
|
return Values.Contains(value);
|
|
}
|
|
|
|
public K GetKey(K key)
|
|
{
|
|
if (TryGetKey(key, out K storedKey))
|
|
return storedKey;
|
|
throw new KeyNotFoundException();
|
|
}
|
|
public bool TryGetKey(K key,out K storedKey)
|
|
{
|
|
TreeNode treeNode = Find(key);
|
|
if (treeNode == null)
|
|
{
|
|
storedKey = default(K);
|
|
return false;
|
|
} else
|
|
{
|
|
storedKey = treeNode.Key;
|
|
return true;
|
|
}
|
|
|
|
}
|
|
|
|
public void Clear()
|
|
{
|
|
headNode = null;
|
|
count = 0;
|
|
}
|
|
|
|
public K First()
|
|
{
|
|
TreeNode node = First(headNode);
|
|
if (node != null)
|
|
return node.Key;
|
|
throw new KeyNotFoundException("BTree is empty");
|
|
}
|
|
public V FirstValue()
|
|
{
|
|
TreeNode node = First(headNode);
|
|
if (node != null)
|
|
return node.Value;
|
|
throw new KeyNotFoundException("BTree is empty");
|
|
}
|
|
public bool TryGetFirstValue(out V value)
|
|
{
|
|
TreeNode node = First(headNode);
|
|
if (node != null)
|
|
{
|
|
value = node.Value;
|
|
return true;
|
|
}
|
|
value = default(V);
|
|
return false;
|
|
}
|
|
|
|
public K Last()
|
|
{
|
|
TreeNode node = Last(headNode);
|
|
if (node != null)
|
|
return node.Key;
|
|
throw new KeyNotFoundException("BTree is empty");
|
|
}
|
|
public V LastValue()
|
|
{
|
|
TreeNode node = Last(headNode);
|
|
if (node != null)
|
|
return node.Value;
|
|
throw new KeyNotFoundException("BTree is empty");
|
|
}
|
|
public bool TryGetLastValue(out V value)
|
|
{
|
|
TreeNode node = Last(headNode);
|
|
if (node != null)
|
|
{
|
|
value = node.Value;
|
|
return true;
|
|
}
|
|
value = default(V);
|
|
return false;
|
|
}
|
|
|
|
public K Previous(K current)
|
|
{
|
|
TreeNode node = Previous(Find(current));
|
|
if (node != null)
|
|
return node.Key;
|
|
throw new KeyNotFoundException();
|
|
}
|
|
public bool TryGetPrevious(K current, out K previous)
|
|
{
|
|
TreeNode node = Previous(Find(current));
|
|
if (node != null)
|
|
{
|
|
previous = node.Key;
|
|
return true;
|
|
}
|
|
previous = default(K);
|
|
return false;
|
|
}
|
|
public bool TryGetPreviousValue(K current, out V previous)
|
|
{
|
|
TreeNode node = Previous(Find(current));
|
|
if (node != null)
|
|
{
|
|
previous = node.Value;
|
|
return true;
|
|
}
|
|
previous = default(V);
|
|
return false;
|
|
}
|
|
|
|
public K Next(K current)
|
|
{
|
|
TreeNode node = Next(Find(current));
|
|
if (node != null)
|
|
return node.Key;
|
|
throw new KeyNotFoundException();
|
|
}
|
|
public bool TryGetNext(K current, out K next)
|
|
{
|
|
TreeNode node = Next(Find(current));
|
|
if (node != null)
|
|
{
|
|
next = node.Key;
|
|
return true;
|
|
}
|
|
next = default(K);
|
|
return false;
|
|
}
|
|
public bool TryGetNextValue(K current, out V next)
|
|
{
|
|
TreeNode node = Next(Find(current));
|
|
if (node != null)
|
|
{
|
|
next = node.Value;
|
|
return true;
|
|
}
|
|
next = default(V);
|
|
return false;
|
|
}
|
|
|
|
public K PreviousOrCurrent(K current)
|
|
{
|
|
if (!TryGetPreviousOrCurrent(current, out K previousOrCurrent))
|
|
throw new KeyNotFoundException();
|
|
return previousOrCurrent;
|
|
}
|
|
public bool TryGetPreviousOrCurrent(K current, out K previousOrCurrent)
|
|
{
|
|
if (Empty)
|
|
{
|
|
previousOrCurrent = default(K);
|
|
return false;
|
|
}
|
|
|
|
TreeNode next = FindFirstGE(current);
|
|
if (next == null)
|
|
previousOrCurrent = Last();
|
|
else
|
|
previousOrCurrent = Previous(next).Key;
|
|
|
|
return true;
|
|
}
|
|
public bool TryGetPreviousOrCurrentValue(K current, out V previousOrCurrentValue)
|
|
{
|
|
if (Empty)
|
|
{
|
|
previousOrCurrentValue = default(V);
|
|
return false;
|
|
}
|
|
|
|
TreeNode next = FindFirstGE(current);
|
|
if (next == null)
|
|
previousOrCurrentValue = LastValue();
|
|
else
|
|
{
|
|
TreeNode previousNode = Previous(next);
|
|
if (previousNode == null)
|
|
previousOrCurrentValue = FirstValue();
|
|
else
|
|
previousOrCurrentValue = previousNode.Value;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
public bool TryGetNextOrCurrent(K current, out K nextOrCurrent)
|
|
{
|
|
if (Empty)
|
|
{
|
|
nextOrCurrent = default(K);
|
|
return false;
|
|
}
|
|
|
|
TreeNode next = FindFirstGE(current);
|
|
if (next == null)
|
|
{
|
|
nextOrCurrent = default(K);
|
|
return false;
|
|
}
|
|
|
|
nextOrCurrent = next.Key;
|
|
return true;
|
|
}
|
|
public bool TryGetNextOrCurrentValue(K current, out V nextOrCurrentValue)
|
|
{
|
|
if (Empty)
|
|
{
|
|
nextOrCurrentValue = default(V);
|
|
return false;
|
|
}
|
|
|
|
TreeNode next = FindFirstGE(current);
|
|
if (next == null)
|
|
{
|
|
nextOrCurrentValue = default(V);
|
|
return false;
|
|
}
|
|
|
|
nextOrCurrentValue = next.Value;
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
private TreeNode First(TreeNode node)
|
|
{
|
|
if (object.ReferenceEquals(node, null))
|
|
return null;
|
|
|
|
while (node.Left != null)
|
|
node = node.Left;
|
|
return node;
|
|
}
|
|
|
|
private TreeNode Last(TreeNode node)
|
|
{
|
|
if (object.ReferenceEquals(node, null))
|
|
return null;
|
|
|
|
while (node.Right != null)
|
|
node = node.Right;
|
|
return node;
|
|
}
|
|
|
|
private TreeNode Find(K key)
|
|
{
|
|
TreeNode node = headNode;
|
|
|
|
while (node != null)
|
|
{
|
|
int comp = Comparison(key, node.Key);
|
|
if (comp == 0)
|
|
return node;
|
|
else if (comp < 0)
|
|
node = node.Left;
|
|
else
|
|
node = node.Right;
|
|
}
|
|
return null;
|
|
}
|
|
private TreeNode FindFirstGE(K key)
|
|
{
|
|
TreeNode node = headNode;
|
|
TreeNode minNode = null;
|
|
|
|
while (node != null)
|
|
{
|
|
if ((Comparison(node.Key, key) >= 0) && ((minNode == null) || (Comparison(node.Key, minNode.Key) < 0)))
|
|
minNode = node;
|
|
|
|
int comp = Comparison(key, node.Key);
|
|
if (comp == 0)
|
|
return node;
|
|
else if (comp < 0)
|
|
{
|
|
if (node.Left == null)
|
|
return minNode;
|
|
node = node.Left;
|
|
}
|
|
else
|
|
{
|
|
if (node.Right == null)
|
|
return minNode;
|
|
node = node.Right;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private TreeNode Previous(TreeNode node)
|
|
{
|
|
if (node.Left != null)
|
|
return Last(node.Left);
|
|
|
|
while (node != null)
|
|
{
|
|
if (object.ReferenceEquals(node.Parent, null))
|
|
return null;
|
|
|
|
if (node.Parent.Right == node)
|
|
return node.Parent;
|
|
|
|
node = node.Parent;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private TreeNode Next(TreeNode node)
|
|
{
|
|
if (node.Right != null)
|
|
return First(node.Right);
|
|
|
|
while (node != null)
|
|
{
|
|
if (object.ReferenceEquals(node.Parent, null))
|
|
return null;
|
|
|
|
if (node.Parent.Left == node)
|
|
return node.Parent;
|
|
|
|
node = node.Parent;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private void Insert(TreeNode newNode)
|
|
{
|
|
if (object.ReferenceEquals(headNode, null))
|
|
{
|
|
headNode = newNode;
|
|
}
|
|
else
|
|
{
|
|
TreeNode node = headNode;
|
|
while (true)
|
|
{
|
|
int c = Comparison(newNode.Key, node.Key);
|
|
if (c == 0)
|
|
{
|
|
throw new ArgumentException("Key exists");
|
|
}
|
|
else if (c < 0)
|
|
{
|
|
if (node.Left == null)
|
|
{
|
|
newNode.Attach(node);
|
|
Balance(node.Parent);
|
|
return;
|
|
}
|
|
else
|
|
node = node.Left;
|
|
}
|
|
else
|
|
{
|
|
if (node.Right == null)
|
|
{
|
|
newNode.Attach(node);
|
|
Balance(node.Parent);
|
|
return;
|
|
}
|
|
else
|
|
node = node.Right;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void Remove(TreeNode node)
|
|
{
|
|
TreeNode p = node.Parent;
|
|
|
|
if ((node.Left != null) && (node.Right != null))
|
|
{
|
|
TreeNode replace = Last(node.Left);
|
|
TreeNode replaceParent = replace.Parent;
|
|
|
|
if (replaceParent == node)
|
|
{
|
|
node.Detach();
|
|
node.Right.Attach(node.Left);
|
|
node.Left.Attach(p);
|
|
}
|
|
else
|
|
{
|
|
TreeNode left = node.Left;
|
|
TreeNode right = node.Right;
|
|
|
|
node.Detach();
|
|
if (left != null)
|
|
left.Detach();
|
|
if (right != null)
|
|
right.Detach();
|
|
|
|
replace.Detach();
|
|
if (replace.Left != null)
|
|
replace.Left.Attach(replaceParent);
|
|
|
|
replace.Attach(p);
|
|
if (left != null)
|
|
left.Attach(replace);
|
|
if (right != null)
|
|
right.Attach(replace);
|
|
}
|
|
|
|
Balance(replace);
|
|
} else
|
|
{
|
|
node.Detach();
|
|
if (node.Left != null)
|
|
node.Left.Attach(p);
|
|
else if (node.Right != null)
|
|
node.Right.Attach(p);
|
|
|
|
Balance(p);
|
|
}
|
|
}
|
|
|
|
private void RotateRight(TreeNode node)
|
|
{
|
|
TreeNode l = node.Left;
|
|
TreeNode p = node.Parent;
|
|
|
|
node.Detach();
|
|
l.Attach(p);
|
|
if (l.Right!= null)
|
|
l.Right.Attach(node);
|
|
node.Attach(l);
|
|
}
|
|
private void RotateLeft(TreeNode node)
|
|
{
|
|
TreeNode r = node.Right;
|
|
TreeNode p = node.Parent;
|
|
|
|
node.Detach();
|
|
r.Attach(p);
|
|
if (r.Left != null)
|
|
r.Left.Attach(node);
|
|
node.Attach(r);
|
|
}
|
|
|
|
private void Balance(TreeNode node)
|
|
{
|
|
while (node != null)
|
|
{
|
|
TreeNode p = node.Parent;
|
|
int myBalance = node.LeftDepth - node.RightDepth;
|
|
if (myBalance + limBalance < 0)
|
|
{
|
|
RotateLeft(node);
|
|
}
|
|
else if (myBalance - limBalance > 0)
|
|
{
|
|
RotateRight(node);
|
|
}
|
|
node = p;
|
|
}
|
|
}
|
|
|
|
public IEnumerable<K> KeysFrom(K first)
|
|
{
|
|
TreeNode node = Find(first);
|
|
while (node != null)
|
|
{
|
|
yield return node.Key;
|
|
node = Next(node);
|
|
}
|
|
}
|
|
|
|
public IEnumerable<K> KeysGreaterEqual(K first)
|
|
{
|
|
TreeNode node = FindFirstGE(first);
|
|
while (node != null)
|
|
{
|
|
yield return node.Key;
|
|
node = Next(node);
|
|
}
|
|
}
|
|
|
|
public void Add(object key, object value) => Add((K)key, (V)value);
|
|
public void Remove(object key) => Remove((K)key);
|
|
public bool ContainsKey(object key) => ContainsKey((K)key);
|
|
public object this[object key] { get => this[(K)key]; set => this[(K)key] = (V)value; }
|
|
|
|
public IEnumerable<K> Keys
|
|
{
|
|
get
|
|
{
|
|
TreeNode node = First(headNode);
|
|
while (node != null)
|
|
{
|
|
yield return node.Key;
|
|
node = Next(node);
|
|
}
|
|
}
|
|
}
|
|
public IEnumerable<V> Values
|
|
{
|
|
get
|
|
{
|
|
TreeNode node = First(headNode);
|
|
while (node != null)
|
|
{
|
|
yield return node.Value;
|
|
node = Next(node);
|
|
}
|
|
}
|
|
}
|
|
|
|
IEnumerable IDict.Keys => Keys;
|
|
IEnumerable IDict.Values => Values;
|
|
|
|
public IEnumerable<KeyValuePair<K, V>> GetInterval(K start, K end) => GetInterval(start, end, null);
|
|
|
|
public IEnumerable<KeyValuePair<K, V>> GetInterval(K start, K end, Func<K,V,bool> filter)
|
|
{
|
|
TreeNode currentNode = (start is null) ? First(headNode) : FindFirstGE(start);
|
|
if (currentNode != null)
|
|
{
|
|
while ((currentNode is not null) && ((end is null) || (Comparison(currentNode.Key, end) <= 0)))
|
|
{
|
|
if (filter is null || filter(currentNode.Key, currentNode.Value))
|
|
yield return new KeyValuePair<K, V>(currentNode.Key, currentNode.Value);
|
|
currentNode = Next(currentNode);
|
|
}
|
|
}
|
|
}
|
|
|
|
class TreeNode
|
|
{
|
|
public BTree<K, V> Tree { get; }
|
|
|
|
public K Key { get; private set; }
|
|
public V Value { get; set; }
|
|
|
|
public TreeNode Parent { get; private set; }
|
|
|
|
public int Depth => 1 + Math.Max(LeftDepth, RightDepth);
|
|
|
|
//public int LeftDepth => Left != null ? Left.Depth : 0;
|
|
//public int RightDepth => Right != null ? Right.Depth : 0;
|
|
public int LeftDepth => leftDepth;
|
|
public int RightDepth => rightDepth;
|
|
|
|
public TreeNode Left { get; private set; }
|
|
public TreeNode Right { get; private set; }
|
|
|
|
private int leftDepth = 0;
|
|
private int rightDepth = 0;
|
|
|
|
|
|
public TreeNode(BTree<K, V> tree, K key, V value)
|
|
{
|
|
Tree = tree;
|
|
Key = key;
|
|
Value = value;
|
|
}
|
|
|
|
public void Attach(TreeNode parent)
|
|
{
|
|
if (!object.ReferenceEquals(this.Parent, null))
|
|
Detach();
|
|
|
|
if (parent == null)
|
|
{
|
|
Tree.headNode = this;
|
|
Parent = null;
|
|
}
|
|
else
|
|
{
|
|
int comp = Tree.Comparison(Key, parent.Key);
|
|
if (comp == 0)
|
|
throw new ArgumentException(String.Format("Key already exists in BTree ({0})",Key));
|
|
else if (comp < 0)
|
|
{
|
|
if (parent.Left != null)
|
|
throw new InvalidOperationException("Another Key is already attached to Parent (left)");
|
|
parent.Left = this;
|
|
parent.leftDepth = Depth + 1;
|
|
}
|
|
else
|
|
{
|
|
if (parent.Right != null)
|
|
throw new InvalidOperationException("Another Key is already attached to Parent (right)");
|
|
parent.Right = this;
|
|
parent.rightDepth = Depth + 1;
|
|
}
|
|
Parent = parent;
|
|
}
|
|
}
|
|
public void Detach()
|
|
{
|
|
if (this.Parent != null)
|
|
{
|
|
if (this.Parent.Left == this)
|
|
{
|
|
this.Parent.Left = null;
|
|
this.Parent.leftDepth = 0;
|
|
}
|
|
else if (this.Parent.Right == this)
|
|
{
|
|
this.Parent.Right = null;
|
|
this.Parent.rightDepth = 0;
|
|
}
|
|
|
|
Parent = null;
|
|
} else if (Tree.headNode == this)
|
|
{
|
|
Tree.headNode = null;
|
|
}
|
|
}
|
|
|
|
public override string ToString()
|
|
{
|
|
return String.Format("[TreeNode Key={0} Value={1}]",Key,Value);
|
|
}
|
|
|
|
}
|
|
|
|
public IEnumerator<KeyValuePair<K, V>> GetEnumerator()
|
|
{
|
|
TreeNode node = First(headNode);
|
|
while (node != null)
|
|
{
|
|
yield return new KeyValuePair<K, V>(node.Key, node.Value);
|
|
node = Next(node);
|
|
}
|
|
}
|
|
|
|
IEnumerator IEnumerable.GetEnumerator()
|
|
{
|
|
return GetEnumerator();
|
|
}
|
|
}
|
|
|
|
public class BTree<K> : BTree<K,object>
|
|
{
|
|
public BTree()
|
|
{
|
|
}
|
|
public BTree(Comparison<K> comparison)
|
|
:base(comparison)
|
|
{
|
|
}
|
|
|
|
public void Add(K key)
|
|
{
|
|
Add(key, null);
|
|
}
|
|
public void TryAdd(K key)
|
|
{
|
|
TryAdd(key, null);
|
|
}
|
|
|
|
public void AddRange(IEnumerable<K> keys)
|
|
{
|
|
foreach (K key in keys)
|
|
{
|
|
Add(key, null);
|
|
}
|
|
}
|
|
public void TryAddRange(IEnumerable<K> keys)
|
|
{
|
|
foreach (K key in keys)
|
|
{
|
|
TryAdd(key, null);
|
|
}
|
|
}
|
|
}
|
|
}
|