Skip to content

Instantly share code, notes, and snippets.

@sebastianfdez
Created April 29, 2020 14:58
Show Gist options
  • Save sebastianfdez/a1d3b4b30145d014cbc774cfe53641ae to your computer and use it in GitHub Desktop.
Save sebastianfdez/a1d3b4b30145d014cbc774cfe53641ae to your computer and use it in GitHub Desktop.
BTree with remove function
/**
* btree namespace.
* @type {BTree}
*/
export default class BTree {
constructor(order) {
/** @type {number} */
this.order = order;
/**
* Root node of the tree.
* @type {BTreeNode}
*/
this.root;
}
/**
* Deletes the value from the Tree. O(log N)
* @param {number} value
*/
delete(value) {
if (this.root.n === 1 && !this.root.leaf &&
this.root.children[0].n === this.order-1 && this.root.children[1].n === this.order -1) {
// Check if the root can shrink the tree into its childs
this.mergeNodes(this.root.children[1], this.root.children[0]);
this.root = this.root.children[0];
}
// Start looking for the value to delete
this.deleteFromNode(this.root, parseInt(value));
}
/**
* Delete a value from a node
* @param {BTreeNode} node
* @param {number} value
*/
deleteFromNode(node, value) {
// Check if value is in the actual node
const index = node.values.indexOf(value);
if (index >= 0) {
// Value present in the node
if (node.leaf && node.n > this.order - 1) {
// If the node is a leaf and has more than order-1 values, just delete it
node.removeValue(node.values.indexOf(value));
return;
}
// Check if one children has enough values to transfer
if (node.children[index].n > this.order - 1 ||
node.children[index + 1].n > this.order - 1) {
// One of the immediate children has enough values to transfer
if (node.children[index].n > this.order - 1) {
// Replace the target value for the higher of left node.
// Then delete that value from the child
const predecessor = this.getMinMaxFromSubTree(node.children[index], 1);
node.values[index] = predecessor;
return this.deleteFromNode(node.children[index], predecessor);
}
const successor = this.getMinMaxFromSubTree(node.children[index+1], 0);
node.values[index] = successor;
return this.deleteFromNode(node.children[index+1], successor);
}
// Children has not enough values to transfer. Do a merge
this.mergeNodes(node.children[index + 1], node.children[index]);
return this.deleteFromNode(node.children[index], value);
}
// Value is not present in the node
if (node.leaf) {
// Value is not in the tree
return;
}
// Value is not present in the node, search in the children
let nextNode = 0;
while (nextNode < node.n && node.values[nextNode] < value) {
nextNode++;
}
if (node.children[nextNode].n > this.order - 1) {
// Child node has enough values to continue
return this.deleteFromNode(node.children[nextNode], value);
}
// Child node has not enough values to continue
// Before visiting next node transfer a value or merge it with a brother
if ((nextNode > 0 && node.children[nextNode - 1].n > this.order - 1) ||
(nextNode < node.n && node.children[nextNode + 1].n > this.order - 1)) {
// One of the immediate children has enough values to transfer
if (nextNode > 0 && node.children[nextNode - 1].n > this.order - 1) {
this.transferValue(node.children[nextNode - 1], node.children[nextNode]);
} else {
this.transferValue(node.children[nextNode + 1], node.children[nextNode]);
}
return this.deleteFromNode(node.children[nextNode], value);
}
// No immediate brother with enough values.
// Merge node with immediate one brother
this.mergeNodes(
nextNode > 0 ? node.children[nextNode - 1] : node.children[nextNode + 1],
node.children[nextNode]);
return this.deleteFromNode(node.children[nextNode], value);
}
/**
* Transfer one value from the origin to the target.
* @param {BTreeNode} origin
* @param {BTreeNode} target
*/
transferValue(origin, target) {
const indexo = origin.parent.children.indexOf(origin);
const indext = origin.parent.children.indexOf(target);
if (indexo < indext) {
target.addValue(target.parent.removeValue(indexo));
origin.parent.addValue(origin.removeValue(origin.n-1));
if (!origin.leaf) {
target.addChild(origin.deleteChild(origin.children.length-1), 0);
}
} else {
target.addValue(target.parent.removeValue(indext));
origin.parent.addValue(origin.removeValue(0));
if (!origin.leaf) {
target.addChild(origin.deleteChild(0), target.children.length);
}
}
}
/**
* Merge 2 nodes into one with the parent median value. O(1)
* @param {BTreeNode} origin
* @param {BTreeNode} target
*/
mergeNodes(origin, target) {
const indexo = origin.parent.children.indexOf(origin);
const indext = target.parent.children.indexOf(target);
target.addValue(target.parent.removeValue(Math.min(indexo, indext)));
for (let i = origin.n - 1; i >= 0; i--) {
target.addValue(origin.removeValue(i));
}
// Remove reference to origin node
target.parent.deleteChild(indexo);
// Transfer all the children from origin node to target
if (!origin.leaf) {
while (origin.children.length) {
indexo > indext ?
target.addChild(origin.deleteChild(0), target.children.length) :
target.addChild(origin.deleteChild(origin.children.length-1), 0);
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment