using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
namespace Advanced.Algorithms.DataStructures
{
///
/// A B-tree implementation.
///
public class BTree : IEnumerable where T : IComparable
{
private readonly int maxKeysPerNode;
private int minKeysPerNode;
internal BTreeNode Root;
public int Count { get; private set; }
public BTree(int maxKeysPerNode)
{
if (maxKeysPerNode < 3)
{
throw new Exception("Max keys per node should be atleast 3.");
}
this.maxKeysPerNode = maxKeysPerNode;
this.minKeysPerNode = maxKeysPerNode / 2;
}
///
/// Time complexity: O(log(n)).
///
public T Max
{
get
{
if (Root == null) return default(T);
var maxNode = findMaxNode(Root);
return maxNode.Keys[maxNode.KeyCount - 1];
}
}
///
/// Time complexity: O(log(n)).
///
public T Min
{
get
{
if (Root == null) return default(T);
var minNode = findMinNode(Root);
return minNode.Keys[0];
}
}
///
/// Time complexity: O(log(n)).
///
public bool HasItem(T value)
{
return find(Root, value) != null;
}
///
/// Find the value node under given node.
///
private BTreeNode find(BTreeNode node, T value)
{
//if leaf then its time to insert
if (node.IsLeaf)
{
for (var i = 0; i < node.KeyCount; i++)
{
if (value.CompareTo(node.Keys[i]) == 0)
{
return node;
}
}
}
else
{
//if not leaf then drill down to leaf
for (var i = 0; i < node.KeyCount; i++)
{
if (value.CompareTo(node.Keys[i]) == 0)
{
return node;
}
//current value is less than new value
//drill down to left child of current value
if (value.CompareTo(node.Keys[i]) < 0)
{
return find(node.Children[i], value);
}
//current value is grearer than new value
//and current value is last element
if (node.KeyCount == i + 1)
{
return find(node.Children[i + 1], value);
}
}
}
return null;
}
///
/// Time complexity: O(log(n)).
///
public void Insert(T newValue)
{
if (Root == null)
{
Root = new BTreeNode(maxKeysPerNode, null) { Keys = { [0] = newValue } };
Root.KeyCount++;
Count++;
return;
}
var leafToInsert = findInsertionLeaf(Root, newValue);
insertAndSplit(ref leafToInsert, newValue, null, null);
Count++;
}
///
/// Find the leaf node to start initial insertion
///
private BTreeNode findInsertionLeaf(BTreeNode node, T newValue)
{
//if leaf then its time to insert
if (node.IsLeaf)
{
return node;
}
//if not leaf then drill down to leaf
for (var i = 0; i < node.KeyCount; i++)
{
//current value is less than new value
//drill down to left child of current value
if (newValue.CompareTo(node.Keys[i]) < 0)
{
return findInsertionLeaf(node.Children[i], newValue);
}
//current value is grearer than new value
//and current value is last element
if (node.KeyCount == i + 1)
{
return findInsertionLeaf(node.Children[i + 1], newValue);
}
}
return node;
}
///
/// Insert and split recursively up until no split is required
///
private void insertAndSplit(ref BTreeNode node, T newValue,
BTreeNode newValueLeft, BTreeNode newValueRight)
{
//add new item to current node
if (node == null)
{
node = new BTreeNode(maxKeysPerNode, null);
Root = node;
}
//newValue have room to fit in this node
//so just insert in right spot in asc order of keys
if (node.KeyCount != maxKeysPerNode)
{
insertToNotFullNode(ref node, newValue, newValueLeft, newValueRight);
return;
}
//if node is full then split node
//and then insert new median to parent.
//divide the current node values + new Node as left and right sub nodes
var left = new BTreeNode(maxKeysPerNode, null);
var right = new BTreeNode(maxKeysPerNode, null);
//median of current Node
var currentMedianIndex = node.GetMedianIndex();
//init currentNode under consideration to left
var currentNode = left;
var currentNodeIndex = 0;
//new Median also takes new Value in to Account
var newMedian = default(T);
var newMedianSet = false;
var newValueInserted = false;
//keep track of each insertion
var insertionCount = 0;
//insert newValue and existing values in sorted order
//to left and right nodes
//set new median during sorting
for (var i = 0; i < node.KeyCount; i++)
{
//if insertion count reached new median
//set the new median by picking the next smallest value
if (!newMedianSet && insertionCount == currentMedianIndex)
{
newMedianSet = true;
//median can be the new value or node.keys[i] (next node key)
//whichever is smaller
if (!newValueInserted && newValue.CompareTo(node.Keys[i]) < 0)
{
//median is new value
newMedian = newValue;
newValueInserted = true;
if (newValueLeft != null)
{
setChild(currentNode, currentNode.KeyCount, newValueLeft);
}
//now fill right node
currentNode = right;
currentNodeIndex = 0;
if (newValueRight != null)
{
setChild(currentNode, 0, newValueRight);
}
i--;
insertionCount++;
continue;
}
//median is next node
newMedian = node.Keys[i];
//now fill right node
currentNode = right;
currentNodeIndex = 0;
continue;
}
//pick the smaller among newValue and node.Keys[i]
//and insert in to currentNode (left and right nodes)
//if new Value was already inserted then just copy from node.Keys in sequence
//since node.Keys is already in sorted order it should be fine
if (newValueInserted || node.Keys[i].CompareTo(newValue) < 0)
{
currentNode.Keys[currentNodeIndex] = node.Keys[i];
currentNode.KeyCount++;
//if child is set don't set again
//the child was already set by last newValueRight or last node
if (currentNode.Children[currentNodeIndex] == null)
{
setChild(currentNode, currentNodeIndex, node.Children[i]);
}
setChild(currentNode, currentNodeIndex + 1, node.Children[i + 1]);
}
else
{
currentNode.Keys[currentNodeIndex] = newValue;
currentNode.KeyCount++;
setChild(currentNode, currentNodeIndex, newValueLeft);
setChild(currentNode, currentNodeIndex + 1, newValueRight);
i--;
newValueInserted = true;
}
currentNodeIndex++;
insertionCount++;
}
//could be that thew newKey is the greatest
//so insert at end
if (!newValueInserted)
{
currentNode.Keys[currentNodeIndex] = newValue;
currentNode.KeyCount++;
setChild(currentNode, currentNodeIndex, newValueLeft);
setChild(currentNode, currentNodeIndex + 1, newValueRight);
}
//insert overflow element (newMedian) to parent
var parent = node.Parent;
insertAndSplit(ref parent, newMedian, left, right);
}
///
/// Insert to a node that is not full
///
private void insertToNotFullNode(ref BTreeNode node, T newValue,
BTreeNode newValueLeft, BTreeNode newValueRight)
{
var inserted = false;
//insert in sorted order
for (var i = 0; i < node.KeyCount; i++)
{
if (newValue.CompareTo(node.Keys[i]) >= 0)
{
continue;
}
insertAt(node.Keys, i, newValue);
node.KeyCount++;
//Insert children if any
setChild(node, i, newValueLeft);
insertChild(node, i + 1, newValueRight);
inserted = true;
break;
}
//newValue is the greatest
//element should be inserted at the end then
if (inserted)
{
return;
}
node.Keys[node.KeyCount] = newValue;
node.KeyCount++;
setChild(node, node.KeyCount - 1, newValueLeft);
setChild(node, node.KeyCount, newValueRight);
}
///
/// Time complexity: O(log(n)).
///
public void Delete(T value)
{
var node = findDeletionNode(Root, value);
if (node == null)
{
throw new Exception("Item do not exist in this tree.");
}
for (var i = 0; i < node.KeyCount; i++)
{
if (value.CompareTo(node.Keys[i]) != 0)
{
continue;
}
//if node is leaf and no underflow
//then just remove the node
if (node.IsLeaf)
{
removeAt(node.Keys, i);
node.KeyCount--;
balance(node);
}
else
{
//replace with max node of left tree
var maxNode = findMaxNode(node.Children[i]);
node.Keys[i] = maxNode.Keys[maxNode.KeyCount - 1];
removeAt(maxNode.Keys, maxNode.KeyCount - 1);
maxNode.KeyCount--;
balance(maxNode);
}
Count--;
return;
}
}
///
/// return the node containing max value which will be a leaf at the right most
///
private BTreeNode findMinNode(BTreeNode node)
{
//if leaf return node
return node.IsLeaf ? node : findMinNode(node.Children[0]);
}
///
/// return the node containing max value which will be a leaf at the right most
///
private BTreeNode findMaxNode(BTreeNode node)
{
//if leaf return node
return node.IsLeaf ? node : findMaxNode(node.Children[node.KeyCount]);
}
///
/// Balance a node which is short of Keys by rotations or merge
///
private void balance(BTreeNode node)
{
if (node == Root || node.KeyCount >= minKeysPerNode)
{
return;
}
var rightSibling = getRightSibling(node);
if (rightSibling != null
&& rightSibling.KeyCount > minKeysPerNode)
{
leftRotate(node, rightSibling);
return;
}
var leftSibling = getLeftSibling(node);
if (leftSibling != null
&& leftSibling.KeyCount > minKeysPerNode)
{
rightRotate(leftSibling, node);
return;
}
if (rightSibling != null)
{
sandwich(node, rightSibling);
}
else
{
sandwich(leftSibling, node);
}
}
///
/// merge two adjacent siblings to one node
///
private void sandwich(BTreeNode leftSibling, BTreeNode rightSibling)
{
var separatorIndex = getNextSeparatorIndex(leftSibling);
var parent = leftSibling.Parent;
var newNode = new BTreeNode(maxKeysPerNode, leftSibling.Parent);
var newIndex = 0;
for (var i = 0; i < leftSibling.KeyCount; i++)
{
newNode.Keys[newIndex] = leftSibling.Keys[i];
if (leftSibling.Children[i] != null)
{
setChild(newNode, newIndex, leftSibling.Children[i]);
}
if (leftSibling.Children[i + 1] != null)
{
setChild(newNode, newIndex + 1, leftSibling.Children[i + 1]);
}
newIndex++;
}
//special case when left sibling is empty
if (leftSibling.KeyCount == 0 && leftSibling.Children[0] != null)
{
setChild(newNode, newIndex, leftSibling.Children[0]);
}
newNode.Keys[newIndex] = parent.Keys[separatorIndex];
newIndex++;
for (var i = 0; i < rightSibling.KeyCount; i++)
{
newNode.Keys[newIndex] = rightSibling.Keys[i];
if (rightSibling.Children[i] != null)
{
setChild(newNode, newIndex, rightSibling.Children[i]);
}
if (rightSibling.Children[i + 1] != null)
{
setChild(newNode, newIndex + 1, rightSibling.Children[i + 1]);
}
newIndex++;
}
//special case when left sibling is empty
if (rightSibling.KeyCount == 0 && rightSibling.Children[0] != null)
{
setChild(newNode, newIndex, rightSibling.Children[0]);
}
newNode.KeyCount = newIndex;
setChild(parent, separatorIndex, newNode);
removeAt(parent.Keys, separatorIndex);
parent.KeyCount--;
removeChild(parent, separatorIndex + 1);
if (parent.KeyCount == 0
&& parent == Root)
{
Root = newNode;
Root.Parent = null;
if (Root.KeyCount == 0)
{
Root = null;
}
return;
}
if (parent.KeyCount < minKeysPerNode)
{
balance(parent);
}
}
///
/// do a right rotation
///
private void rightRotate(BTreeNode leftSibling, BTreeNode rightSibling)
{
var parentIndex = getNextSeparatorIndex(leftSibling);
insertAt(rightSibling.Keys, 0, rightSibling.Parent.Keys[parentIndex]);
rightSibling.KeyCount++;
insertChild(rightSibling, 0, leftSibling.Children[leftSibling.KeyCount]);
rightSibling.Parent.Keys[parentIndex] = leftSibling.Keys[leftSibling.KeyCount - 1];
removeAt(leftSibling.Keys, leftSibling.KeyCount - 1);
leftSibling.KeyCount--;
removeChild(leftSibling, leftSibling.KeyCount + 1);
}
///
/// do a left rotation
///
private void leftRotate(BTreeNode leftSibling, BTreeNode rightSibling)
{
var parentIndex = getNextSeparatorIndex(leftSibling);
leftSibling.Keys[leftSibling.KeyCount] = leftSibling.Parent.Keys[parentIndex];
leftSibling.KeyCount++;
setChild(leftSibling, leftSibling.KeyCount, rightSibling.Children[0]);
leftSibling.Parent.Keys[parentIndex] = rightSibling.Keys[0];
removeAt(rightSibling.Keys, 0);
rightSibling.KeyCount--;
removeChild(rightSibling, 0);
}
///
/// Locate the node in which the item to delete exist
///
private BTreeNode findDeletionNode(BTreeNode node, T value)
{
//if leaf then its time to insert
if (node.IsLeaf)
{
for (var i = 0; i < node.KeyCount; i++)
{
if (value.CompareTo(node.Keys[i]) == 0)
{
return node;
}
}
}
else
{
//if not leaf then drill down to leaf
for (var i = 0; i < node.KeyCount; i++)
{
if (value.CompareTo(node.Keys[i]) == 0)
{
return node;
}
//current value is less than new value
//drill down to left child of current value
if (value.CompareTo(node.Keys[i]) < 0)
{
return findDeletionNode(node.Children[i], value);
}
//current value is grearer than new value
//and current value is last element
if (node.KeyCount == i + 1)
{
return findDeletionNode(node.Children[i + 1], value);
}
}
}
return null;
}
///
/// Get next key separator index after this child Node in parent
///
private int getNextSeparatorIndex(BTreeNode node)
{
var parent = node.Parent;
if (node.Index == 0)
{
return 0;
}
if (node.Index == parent.KeyCount)
{
return node.Index - 1;
}
return node.Index;
}
///
/// get the right sibling node
///
private BTreeNode getRightSibling(BTreeNode node)
{
var parent = node.Parent;
return node.Index == parent.KeyCount ? null : parent.Children[node.Index + 1];
}
///
/// get left sibling node
///
private BTreeNode getLeftSibling(BTreeNode node)
{
return node.Index == 0 ? null : node.Parent.Children[node.Index - 1];
}
private void setChild(BTreeNode parent, int childIndex, BTreeNode child)
{
parent.Children[childIndex] = child;
if (child == null)
{
return;
}
child.Parent = parent;
child.Index = childIndex;
}
private void insertChild(BTreeNode parent, int childIndex, BTreeNode child)
{
insertAt(parent.Children, childIndex, child);
if (child != null)
{
child.Parent = parent;
}
//update indices
for (var i = childIndex; i <= parent.KeyCount; i++)
{
if (parent.Children[i] != null)
{
parent.Children[i].Index = i;
}
}
}
private void removeChild(BTreeNode parent, int childIndex)
{
removeAt(parent.Children, childIndex);
//update indices
for (var i = childIndex; i <= parent.KeyCount; i++)
{
if (parent.Children[i] != null)
{
parent.Children[i].Index = i;
}
}
}
///
/// Shift array right at index to make room for new insertion
/// And then insert at index
/// Assumes array have atleast one empty index at end
///
private void insertAt(TS[] array, int index, TS newValue)
{
//shift elements right by one indice from index
Array.Copy(array, index, array, index + 1, array.Length - index - 1);
//now set the value
array[index] = newValue;
}
///
/// Shift array left at index
///
private void removeAt(TS[] array, int index)
{
//shift elements right by one indice from index
Array.Copy(array, index + 1, array, index, array.Length - index - 1);
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public IEnumerator GetEnumerator()
{
return new BTreeEnumerator(Root);
}
}
///
/// abstract node shared by both B and B+ tree nodes
/// so that we can use this for common tests across B and B+ tree
///
internal abstract class BNode where T : IComparable
{
///
/// Array Index of this node in parent's Children array
///
internal int Index;
internal T[] Keys { get; set; }
internal int KeyCount;
//for common unit testing across B and B+ tree
internal abstract BNode GetParent();
internal abstract BNode[] GetChildren();
internal BNode(int maxKeysPerNode)
{
Keys = new T[maxKeysPerNode];
}
internal int GetMedianIndex()
{
return (KeyCount / 2) + 1;
}
}
internal class BTreeNode : BNode where T : IComparable
{
internal BTreeNode Parent { get; set; }
internal BTreeNode[] Children { get; set; }
internal bool IsLeaf => Children[0] == null;
internal BTreeNode(int maxKeysPerNode, BTreeNode parent)
: base(maxKeysPerNode)
{
Parent = parent;
Children = new BTreeNode[maxKeysPerNode + 1];
}
///
/// For shared test method accross B and B+ tree
///
internal override BNode GetParent()
{
return Parent;
}
///
/// For shared test method accross B and B+ tree
///
internal override BNode[] GetChildren()
{
return Children;
}
}
internal class BTreeEnumerator : IEnumerator where T : IComparable
{
private readonly BTreeNode root;
private Stack> progress;
private BTreeNode current;
private int index;
internal BTreeEnumerator(BTreeNode root)
{
this.root = root;
}
public bool MoveNext()
{
if (root == null)
{
return false;
}
if (progress == null)
{
current = root;
progress = new Stack>(root.Children.Take(root.KeyCount + 1).Where(x => x != null));
return current.KeyCount > 0;
}
if (current != null && index + 1 < current.KeyCount)
{
index++;
return true;
}
if (progress.Count > 0)
{
index = 0;
current = progress.Pop();
foreach (var child in current.Children.Take(current.KeyCount + 1).Where(x => x != null))
{
progress.Push(child);
}
return true;
}
return false;
}
public void Reset()
{
progress = null;
current = null;
index = 0;
}
object IEnumerator.Current => Current;
public T Current
{
get
{
return current.Keys[index];
}
}
public void Dispose()
{
progress = null;
}
}
}