942 lines
29 KiB
C#
942 lines
29 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
|
|
namespace Best.HTTP.Shared.Databases.Indexing
|
|
{
|
|
public enum Side
|
|
{
|
|
Left,
|
|
Right
|
|
}
|
|
|
|
/// <summary>
|
|
/// Implements most common list functions. With best case (no or only one item) it doesn't do any allocation.
|
|
/// </summary>
|
|
public struct NoAllocList<T>
|
|
{
|
|
private T _value;
|
|
private bool _hasValue;
|
|
|
|
private List<T> _values;
|
|
|
|
public NoAllocList(T value)
|
|
{
|
|
this._value = value;
|
|
this._hasValue = true;
|
|
this._values = null;
|
|
}
|
|
|
|
public T this[int index] {
|
|
get => this._hasValue ? this._value : this._values[index];
|
|
set
|
|
{
|
|
if (index < 0 || (this._values == null && index > 0))
|
|
throw new IndexOutOfRangeException(index.ToString());
|
|
|
|
if (this._values != null)
|
|
this._values[index] = value;
|
|
else
|
|
{
|
|
this._value = value;
|
|
this._hasValue = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
public int Count { get => this._values != null ? this._values.Count : (this._hasValue ? 1 : 0); }
|
|
|
|
public void Add(T item)
|
|
{
|
|
if (this._values != null)
|
|
this._values.Add(item);
|
|
else if (this._hasValue)
|
|
{
|
|
this._values = new List<T> { this._value, item };
|
|
this._value = default(T);
|
|
this._hasValue = false;
|
|
}
|
|
else
|
|
{
|
|
this._value = item;
|
|
this._hasValue = true;
|
|
}
|
|
}
|
|
|
|
public void Clear()
|
|
{
|
|
this._values?.Clear();
|
|
this._values = null;
|
|
this._value = default(T);
|
|
this._hasValue = false;
|
|
}
|
|
|
|
public bool Contains(T item)
|
|
{
|
|
if (this._values != null)
|
|
return this._values.Contains(item);
|
|
|
|
// This can thrown a NullRefException if _value is null!
|
|
return this._hasValue ? this._value.Equals(item) : false;
|
|
}
|
|
|
|
public bool Remove(T item)
|
|
{
|
|
if (this._values != null)
|
|
return this._values.Remove(item);
|
|
else if (this._hasValue && this._value.Equals(item))
|
|
{
|
|
this._value = default(T);
|
|
this._hasValue = false;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public void RemoveAt(int index)
|
|
{
|
|
if (index < 0 || (this._values == null && index > 0))
|
|
throw new IndexOutOfRangeException(index.ToString());
|
|
|
|
if (this._values != null)
|
|
this._values.RemoveAt(index);
|
|
else
|
|
{
|
|
this._value = default(T);
|
|
this._hasValue = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
public sealed class Node<KeyT, ValueT>
|
|
{
|
|
public Node<KeyT, ValueT> Parent, Left, Right;
|
|
|
|
public KeyT Key { get; private set; }
|
|
|
|
/// <summary>
|
|
/// Depth of the node.
|
|
/// </summary>
|
|
public int Depth;
|
|
|
|
/// <summary>
|
|
/// Difference between LeftDepth and RightDepth.
|
|
/// </summary>
|
|
public int BalanceFactor { get { return this.LeftDepth - this.RightDepth; } }
|
|
|
|
/// <summary>
|
|
/// Left node's Depth, or -1 if it's null.
|
|
/// </summary>
|
|
public int LeftDepth { get { return this.Left == null ? -1 : this.Left.Depth; } }
|
|
|
|
/// <summary>
|
|
/// Right node's Depth, or -1 if it's null.
|
|
/// </summary>
|
|
public int RightDepth { get { return this.Right == null ? -1 : this.Right.Depth; } }
|
|
|
|
public bool IsRoot { get { return this.Parent == null; } }
|
|
|
|
public int ChildCount { get { return (this.Left == null ? 0 : 1) + (this.Right == null ? 0 : 1); } }
|
|
|
|
// Stored values aren't public as modifing them requires special care.
|
|
private NoAllocList<ValueT> _item;
|
|
|
|
public Node(Node<KeyT, ValueT> parent, KeyT key, ValueT value)
|
|
{
|
|
this.Parent = parent;
|
|
this.Key = key;
|
|
this._item = new NoAllocList<ValueT>(value);
|
|
|
|
// Depth is 0 by default, as it has no child
|
|
this.Depth = 0;
|
|
}
|
|
|
|
public void BubbleUpDepthChange()
|
|
{
|
|
var current = this;
|
|
|
|
while (current != null)
|
|
{
|
|
var oldDepth = current.Depth;
|
|
current.Depth = Math.Max(current.LeftDepth, current.RightDepth) + 1;
|
|
|
|
if (oldDepth != current.Depth)
|
|
current = current.Parent;
|
|
else
|
|
break;
|
|
}
|
|
}
|
|
|
|
public ValueT this[int index] { get => this._item[index]; }
|
|
|
|
public int Count { get => this._item.Count; }
|
|
|
|
public void Clear() => this._item = new NoAllocList<ValueT>();
|
|
|
|
public void Add(ValueT value)
|
|
{
|
|
var tmp = this._item;
|
|
tmp.Add(value);
|
|
this._item = tmp;
|
|
}
|
|
|
|
public bool Remove(ValueT value)
|
|
{
|
|
var tmp = this._item;
|
|
var result = tmp.Remove(value);
|
|
if (result)
|
|
this._item = tmp;
|
|
return result;
|
|
}
|
|
|
|
public List<ValueT> ToList()
|
|
{
|
|
var list = new List<ValueT>(this._item.Count);
|
|
|
|
for (int i = 0; i < this._item.Count; ++i)
|
|
list.Add(this._item[i]);
|
|
|
|
return list;
|
|
}
|
|
|
|
public override string ToString()
|
|
{
|
|
return $"{this.Left?.Key.ToString()} <- {this.Key.ToString()} -> {this.Right?.Key.ToString()}";
|
|
}
|
|
}
|
|
|
|
// https://www.codesdope.com/course/data-structures-avl-trees/
|
|
public sealed class AVLTree<Key, Value>
|
|
{
|
|
public int ElemCount { get; private set; }
|
|
public int NodeCount { get; private set; }
|
|
public IComparer<Key> Comparer;
|
|
|
|
public Node<Key, Value> RootNode { get; private set; } = null;
|
|
|
|
public AVLTree(IComparer<Key> comparer)
|
|
{
|
|
this.Comparer = comparer;
|
|
}
|
|
|
|
public void Add(Key key, Value item, bool clearValues = false)
|
|
{
|
|
if (this.RootNode == null) {
|
|
this.NodeCount++;
|
|
this.ElemCount++;
|
|
this.RootNode = new Node<Key, Value>(null, key, item);
|
|
return;
|
|
}
|
|
|
|
var current = this.RootNode;
|
|
do
|
|
{
|
|
// +--------------------+-----------------------+
|
|
// | Value | Meaning |
|
|
// +--------------------+-----------------------+
|
|
// | Less than zero | x is less than y. |
|
|
// | Zero | x equals y. |
|
|
// | Greater than zero | x is greater than y. |
|
|
// +--------------------------------------------+
|
|
int comp = this.Comparer.Compare(/*x: */ current.Key, /*y: */ key);
|
|
|
|
// equals
|
|
if (comp == 0)
|
|
{
|
|
if (clearValues)
|
|
{
|
|
this.ElemCount -= current.Count;
|
|
current.Clear();
|
|
}
|
|
|
|
current.Add(item);
|
|
break;
|
|
}
|
|
|
|
// current's key > key
|
|
if (comp > 0)
|
|
{
|
|
// insert new node
|
|
if (current.Left == null)
|
|
{
|
|
current.Left = new Node<Key, Value>(current, key, item);
|
|
current.BubbleUpDepthChange(/*Side.Left, 1*/);
|
|
|
|
current = current.Left;
|
|
|
|
this.NodeCount++;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
current = current.Left;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// current's key < key
|
|
if (comp < 0)
|
|
{
|
|
// insert new node
|
|
if (current.Right == null)
|
|
{
|
|
current.Right = new Node<Key, Value>(current, key, item);
|
|
current.BubbleUpDepthChange(/*Side.Right, 1*/);
|
|
|
|
current = current.Right;
|
|
|
|
this.NodeCount++;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
current = current.Right;
|
|
continue;
|
|
}
|
|
}
|
|
} while (true);
|
|
|
|
this.ElemCount++;
|
|
|
|
while (RebalanceFrom(current) != null)
|
|
;
|
|
|
|
//TestBalance(this.root);
|
|
}
|
|
|
|
public bool TestBalance() => TestBalance(this.RootNode);
|
|
|
|
private bool TestBalance(Node<Key, Value> node)
|
|
{
|
|
if (node == null)
|
|
return true;
|
|
|
|
if (Math.Abs(node.BalanceFactor) > 1)
|
|
{
|
|
//UnityEngine.Debug.Break();
|
|
return false;
|
|
}
|
|
|
|
return TestBalance(node.Left) && TestBalance(node.Right);
|
|
}
|
|
|
|
List<Side> path = new List<Side>(2);
|
|
|
|
private Node<Key, Value> RebalanceFrom(Node<Key, Value> newNode)
|
|
{
|
|
if (newNode.IsRoot || newNode.Parent.IsRoot)
|
|
return null;
|
|
|
|
path.Clear();
|
|
|
|
// find first unbalanced node or exit when found the root node (root still can be unbalanced!)
|
|
var current = newNode;
|
|
var balanceFactor = current.BalanceFactor;
|
|
while (!current.IsRoot && Math.Abs(balanceFactor) <= 1)
|
|
{
|
|
if (current.Parent.Left == current)
|
|
path.Add(Side.Left);
|
|
else
|
|
path.Add(Side.Right);
|
|
|
|
current = current.Parent;
|
|
balanceFactor = current.BalanceFactor;
|
|
}
|
|
|
|
// it's a balanced tree
|
|
if (Math.Abs(balanceFactor) <= 1)
|
|
return null;
|
|
|
|
Side last = path[path.Count - 1];// path[path.StartIdx];
|
|
Side prev = path[path.Count - 2];// path[path.EndIdx];
|
|
|
|
if (last == Side.Left && prev == Side.Left)
|
|
{
|
|
// insertion to a left child of a left child
|
|
RotateRight(current)
|
|
.BubbleUpDepthChange();
|
|
current.BubbleUpDepthChange();
|
|
}
|
|
else if (last == Side.Right && prev == Side.Right)
|
|
{
|
|
// insertion to a right child of a right child
|
|
RotateLeft(current)
|
|
.BubbleUpDepthChange();
|
|
current.BubbleUpDepthChange();
|
|
}
|
|
else if (last == Side.Right && prev == Side.Left)
|
|
{
|
|
// insertion to a left child of a right child
|
|
var current_right = current.Right;
|
|
RotateRight(current.Right);
|
|
RotateLeft(current);
|
|
|
|
current_right.BubbleUpDepthChange();
|
|
current.BubbleUpDepthChange();
|
|
}
|
|
else if (last == Side.Left && prev == Side.Right)
|
|
{
|
|
// insertion to a right child of a left child
|
|
|
|
var current_left = current.Left;
|
|
RotateLeft(current.Left);
|
|
RotateRight(current);
|
|
|
|
current_left.BubbleUpDepthChange();
|
|
current.BubbleUpDepthChange();
|
|
}
|
|
|
|
return current;
|
|
}
|
|
|
|
public void Clear()
|
|
{
|
|
this.RootNode = null;
|
|
this.ElemCount = 0;
|
|
this.NodeCount = 0;
|
|
}
|
|
|
|
public List<Value> Remove(Key key)
|
|
{
|
|
if (this.RootNode == null)
|
|
return null;
|
|
|
|
var current = this.RootNode;
|
|
do
|
|
{
|
|
int comp = this.Comparer.Compare(current.Key, key);
|
|
|
|
// equals
|
|
if (comp == 0)
|
|
{
|
|
this.NodeCount--;
|
|
this.ElemCount -= current.Count;
|
|
|
|
// remove current node from the tree
|
|
RemoveNode(current);
|
|
|
|
return current.ToList();
|
|
}
|
|
|
|
// current's key > key
|
|
if (comp > 0)
|
|
{
|
|
if (current.Left == null)
|
|
return null;
|
|
else
|
|
{
|
|
current = current.Left;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// current's key < key
|
|
if (comp < 0)
|
|
{
|
|
if (current.Right == null)
|
|
return null;
|
|
else
|
|
{
|
|
current = current.Right;
|
|
continue;
|
|
}
|
|
}
|
|
} while (true);
|
|
}
|
|
|
|
public void Remove(Key key, Value value)
|
|
{
|
|
if (this.RootNode == null)
|
|
return;
|
|
|
|
var current = this.RootNode;
|
|
do
|
|
{
|
|
int comp = this.Comparer.Compare(current.Key, key);
|
|
|
|
// equals
|
|
if (comp == 0)
|
|
{
|
|
if (current.Remove(value))
|
|
this.ElemCount--;
|
|
|
|
if (current.Count == 0)
|
|
{
|
|
// remove current node from the tree
|
|
RemoveNode(current);
|
|
|
|
this.NodeCount--;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
// current's key > key
|
|
if (comp > 0)
|
|
{
|
|
if (current.Left == null)
|
|
return ;
|
|
else
|
|
{
|
|
current = current.Left;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// current's key < key
|
|
if (comp < 0)
|
|
{
|
|
if (current.Right == null)
|
|
return ;
|
|
else
|
|
{
|
|
current = current.Right;
|
|
continue;
|
|
}
|
|
}
|
|
} while (true);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Removes node and reparent any child it has.
|
|
/// </summary>
|
|
private void RemoveNode(Node<Key, Value> node)
|
|
{
|
|
var parent = node.Parent;
|
|
Side side = parent?.Left == node ? Side.Left : Side.Right;
|
|
int childCount = node.ChildCount;
|
|
|
|
var testForRebalanceNode = parent;
|
|
|
|
switch(childCount)
|
|
{
|
|
case 0:
|
|
// node has no child
|
|
|
|
if (parent == null)
|
|
{
|
|
this.RootNode = null;
|
|
}
|
|
else
|
|
{
|
|
if (parent.Left == node)
|
|
parent.Left = null;
|
|
else
|
|
parent.Right = null;
|
|
parent.BubbleUpDepthChange();
|
|
}
|
|
|
|
node.Parent = null;
|
|
break;
|
|
|
|
case 1:
|
|
// re-parent the only child
|
|
// Example: Removing node 25 will replace it with 30
|
|
//
|
|
// 20
|
|
// 15 25
|
|
// 30
|
|
//
|
|
var child = node.Left ?? node.Right;
|
|
|
|
if (parent == null)
|
|
{
|
|
this.RootNode = child;
|
|
this.RootNode.Parent = null;
|
|
}
|
|
else
|
|
{
|
|
if (parent.Left == node)
|
|
parent.Left = child;
|
|
else
|
|
parent.Right = child;
|
|
child.Parent = parent;
|
|
|
|
parent.BubbleUpDepthChange();
|
|
}
|
|
break;
|
|
|
|
default:
|
|
// two child
|
|
|
|
// 1: Replace 20 with 25
|
|
//
|
|
// 20 20
|
|
// 15 25 15 25
|
|
// 30
|
|
//
|
|
|
|
// 2: Replace 20 with 22
|
|
//
|
|
// 20
|
|
// 15 25
|
|
// 22
|
|
|
|
// 3: Re-parent 23 for 25, replace 20 with 22
|
|
//
|
|
// 20
|
|
// 15 25
|
|
// 22
|
|
// 23
|
|
|
|
// Cases 1 and 3 are the same, both 25 and 22 has a right child. But in case 3, 22 isn't first child of 20!
|
|
|
|
// Find node with the least Key, that's a node without a left node so we have to deal only with its right node.
|
|
var nodeToReplaceWith = FindMin(node.Right);
|
|
|
|
testForRebalanceNode = nodeToReplaceWith;
|
|
side = Side.Right;
|
|
|
|
// re-parent 23 in case 3:
|
|
if (nodeToReplaceWith.Parent != node)
|
|
{
|
|
testForRebalanceNode = nodeToReplaceWith.Parent;
|
|
if (nodeToReplaceWith.Parent.Left == nodeToReplaceWith)
|
|
{
|
|
nodeToReplaceWith.Parent.Left = nodeToReplaceWith.Right;
|
|
side = Side.Left;
|
|
}
|
|
else
|
|
{
|
|
nodeToReplaceWith.Parent.Right = nodeToReplaceWith.Right;
|
|
side = Side.Right;
|
|
}
|
|
|
|
if (nodeToReplaceWith.Right != null)
|
|
nodeToReplaceWith.Right.Parent = nodeToReplaceWith.Parent;
|
|
}
|
|
|
|
if (parent == null)
|
|
this.RootNode = nodeToReplaceWith;
|
|
else
|
|
{
|
|
if (parent.Left == node)
|
|
parent.Left = nodeToReplaceWith;
|
|
else
|
|
parent.Right = nodeToReplaceWith;
|
|
}
|
|
nodeToReplaceWith.Parent = parent;
|
|
|
|
// Reparent node's left
|
|
nodeToReplaceWith.Left = node.Left;
|
|
node.Left.Parent = nodeToReplaceWith;
|
|
|
|
// Reparent node's right node, if it's not the one we replaceing it with
|
|
if (node.Right != nodeToReplaceWith)
|
|
{
|
|
nodeToReplaceWith.Right = node.Right;
|
|
node.Right.Parent = nodeToReplaceWith;
|
|
}
|
|
//else
|
|
// nodeToReplaceWith.Right = null;
|
|
|
|
if (testForRebalanceNode != nodeToReplaceWith)
|
|
testForRebalanceNode.BubbleUpDepthChange();
|
|
nodeToReplaceWith.BubbleUpDepthChange();
|
|
|
|
break;
|
|
}
|
|
|
|
while (RebalanceForRemoval(testForRebalanceNode, side) != null)
|
|
;
|
|
|
|
//TestBalance(this.root);
|
|
}
|
|
|
|
private Node<Key, Value> RebalanceForRemoval(Node<Key, Value> removedParentNode, Side side)
|
|
{
|
|
if (removedParentNode == null)
|
|
return null;
|
|
|
|
path.Clear();
|
|
path.Add(side);
|
|
|
|
// find first unbalanced node or exit when found the root node (root still can be unbalanced!)
|
|
var current = removedParentNode;
|
|
while (!current.IsRoot && Math.Abs(current.BalanceFactor) <= 1)
|
|
{
|
|
if (current.Parent.Left == current)
|
|
path.Add(Side.Left);
|
|
else
|
|
path.Add(Side.Right);
|
|
|
|
current = current.Parent;
|
|
}
|
|
|
|
// it's a balanced tree
|
|
if (Math.Abs(current.BalanceFactor) <= 1)
|
|
return null;
|
|
|
|
// from what direction we came from
|
|
Side fromDirection = path[path.Count - 1];
|
|
|
|
// check weather it's an inside or outside case
|
|
switch (fromDirection)
|
|
{
|
|
case Side.Right:
|
|
{
|
|
bool isOutside = current.Left.LeftDepth >= current.Left.RightDepth;
|
|
if (isOutside)
|
|
{
|
|
RotateRight(current)
|
|
.BubbleUpDepthChange();
|
|
current.BubbleUpDepthChange();
|
|
}
|
|
else
|
|
{
|
|
var current_left = current.Left;
|
|
RotateLeft(current.Left);
|
|
RotateRight(current);
|
|
|
|
current_left.BubbleUpDepthChange();
|
|
current.BubbleUpDepthChange();
|
|
}
|
|
}
|
|
break;
|
|
|
|
case Side.Left:
|
|
{
|
|
bool isOutside = current.Right.RightDepth >= current.Right.LeftDepth;
|
|
if (isOutside)
|
|
{
|
|
// Example: Removing node 14 result in a disbalance in node 20
|
|
//
|
|
// 20
|
|
// 15 25
|
|
// (14) 22 26
|
|
// 27
|
|
|
|
var current_right = current.Right; // node 25
|
|
RotateLeft(current);
|
|
// After RotateLeft(current: node 20):
|
|
// 25
|
|
// 20 26
|
|
// 15 22 27
|
|
|
|
current.BubbleUpDepthChange(); // node 20
|
|
current_right.BubbleUpDepthChange(); // node 25
|
|
}
|
|
else
|
|
{
|
|
// Example: Removing node 14 results in a disbalance in node 20.
|
|
//
|
|
// 20
|
|
// 15 25
|
|
// (14) 22 26
|
|
// 23
|
|
|
|
var current_right = current.Right;
|
|
|
|
RotateRight(current.Right);
|
|
// After RotateRight(current.Right: node 22):
|
|
// 20
|
|
// 15 22
|
|
// 25
|
|
// 23 26
|
|
|
|
RotateLeft(current);
|
|
// After RotateLeft(current: node 20):
|
|
// 22
|
|
// 20 25
|
|
// 15 23 26
|
|
|
|
current.BubbleUpDepthChange();
|
|
current_right.BubbleUpDepthChange();
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
return current;
|
|
}
|
|
|
|
private Node<Key, Value> FindMin(Node<Key, Value> node)
|
|
{
|
|
var current = node;
|
|
|
|
while (current.Left != null)
|
|
current = current.Left;
|
|
|
|
return current;
|
|
}
|
|
|
|
private Node<Key, Value> FindMax(Node<Key, Value> node)
|
|
{
|
|
var current = node;
|
|
while (current.Right != null)
|
|
current = current.Right;
|
|
|
|
return current;
|
|
}
|
|
|
|
public List<Value> Find(Key key) {
|
|
if (this.RootNode == null)
|
|
return null;
|
|
|
|
var current = this.RootNode;
|
|
do
|
|
{
|
|
int comp = this.Comparer.Compare(current.Key, key);
|
|
|
|
// equals
|
|
if (comp == 0)
|
|
return current.ToList();
|
|
|
|
// current's key > key
|
|
if (comp > 0)
|
|
{
|
|
if (current.Left == null)
|
|
return null;
|
|
else
|
|
{
|
|
current = current.Left;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// current's key < key
|
|
if (comp < 0)
|
|
{
|
|
if (current.Right == null)
|
|
return null;
|
|
else
|
|
{
|
|
current = current.Right;
|
|
continue;
|
|
}
|
|
}
|
|
} while (true);
|
|
}
|
|
|
|
public IEnumerable<Value> WalkHorizontal()
|
|
{
|
|
if (this.RootNode == null)
|
|
yield break;
|
|
|
|
Queue<Node<Key, Value>> toWalk = new Queue<Node<Key, Value>>();
|
|
|
|
toWalk.Enqueue(this.RootNode);
|
|
|
|
while (toWalk.Count > 0)
|
|
{
|
|
var current = toWalk.Dequeue();
|
|
|
|
if (current.Left != null)
|
|
toWalk.Enqueue(current.Left);
|
|
if (current.Right != null)
|
|
toWalk.Enqueue(current.Right);
|
|
|
|
for (int i = 0; i < current.Count; i++)
|
|
yield return current[i];
|
|
}
|
|
}
|
|
|
|
public bool ContainsKey(Key key)
|
|
{
|
|
if (this.RootNode == null)
|
|
return false;
|
|
|
|
var current = this.RootNode;
|
|
do
|
|
{
|
|
int comp = this.Comparer.Compare(current.Key, key);
|
|
|
|
// equals
|
|
if (comp == 0)
|
|
return true;
|
|
|
|
// current's key > key
|
|
if (comp > 0)
|
|
{
|
|
if (current.Left == null)
|
|
return false;
|
|
else
|
|
{
|
|
current = current.Left;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// current's key < key
|
|
if (comp < 0)
|
|
{
|
|
if (current.Right == null)
|
|
return false;
|
|
else
|
|
{
|
|
current = current.Right;
|
|
continue;
|
|
}
|
|
}
|
|
} while (true);
|
|
}
|
|
|
|
private Node<Key, Value> RotateRight(Node<Key, Value> current)
|
|
{
|
|
// Current\
|
|
// 20 15
|
|
// 15 10 20
|
|
// 10 ? ?
|
|
var parent = current.Parent;
|
|
var leftChild = current.Left;
|
|
|
|
// re-parent left child
|
|
if (parent != null)
|
|
{
|
|
if (parent.Left == current)
|
|
parent.Left = leftChild;
|
|
else
|
|
parent.Right = leftChild;
|
|
}
|
|
else
|
|
this.RootNode = leftChild;
|
|
leftChild.Parent = parent;
|
|
|
|
// re-parent left child's right child
|
|
if (leftChild.Right != null)
|
|
leftChild.Right.Parent = current;
|
|
current.Left = leftChild.Right;
|
|
|
|
// re-parent current
|
|
current.Parent = leftChild;
|
|
leftChild.Right = current;
|
|
|
|
// return with the node that took the place of current
|
|
return leftChild;
|
|
}
|
|
|
|
private Node<Key, Value> RotateLeft(Node<Key, Value> current)
|
|
{
|
|
// /Current
|
|
// 20 15
|
|
// 15 20 10
|
|
// ? 10 ?
|
|
var parent = current.Parent;
|
|
var rightChild = current.Right;
|
|
|
|
// re-parent right child
|
|
if (parent != null)
|
|
{
|
|
if (parent.Left == current)
|
|
parent.Left = rightChild;
|
|
else
|
|
parent.Right = rightChild;
|
|
}
|
|
else
|
|
this.RootNode = rightChild;
|
|
rightChild.Parent = parent;
|
|
|
|
// re-parent right child's left child
|
|
if (rightChild.Left != null)
|
|
rightChild.Left.Parent = current;
|
|
current.Right = rightChild.Left;
|
|
|
|
// re-parent current
|
|
current.Parent = rightChild;
|
|
rightChild.Left = current;
|
|
|
|
return rightChild;
|
|
}
|
|
}
|
|
}
|