123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941 |
- 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;
- }
- }
- }
|