class AVLTree extends BinarySearchTree {
constructor (...args) {
super (...args);
this.updateHeight ()
}
updateHeight () {
this.height = this.node ? Math.max(this.node.left.height, this.node.right.height) + 1 : -1
}
get balanceFactor () {
if (this.node) return this.node.right.height - this.node.left.height
return 0
}
findAndModify (key, modify) {
let result;
if (!this.node || this.node.entry.key == key) {
result = modify (this);
} else if (this.node.entry.key < key) {
result = this.node.right.findAndModify(key, modify);
} else {
result = this.node.left.findAndModify (key, modify);
}
this.rebalance()
}
rebalance () {
this.updateHeight ();
let bal = this.balanceFactor;
if (bal < -1) { // Left side too heavy
if (this.node.left.node.left.height < this.node.left.node.right.height)
this.node.left.rotateLeft(); // left-right heavy --> double rotation
this.rotateRight();
}
else if (bal > +1) { // Right side too heavy
if (this.node.right.node.left.height > this.node.right.node.right.height)
this.node.right.rotateRight(); // right-left heavy --> double rotation
this.rotateLeft();
}
}
// AVL right rotation: lifts the node on the left
rotateRight () {
let [d, b, C] = [this.node, this.node.left.node, this.node.left.node.right.node];
[this.node, b.right.node, d.left.node] = [b, d, C];
d.left.updateHeight();
b.right.updateHeight();
this.updateHeight();
}
// AVL left rotation: lifts the node on the right
rotateLeft () {
let [b, d, C] = [this.node, this.node.right.node, this.node.right.node.left.node];
[this.node, d.left.node, b.right.node] = [d, b, C];
b.right.updateHeight();
d.left.updateHeight();
this.updateHeight();
}
// Inserts the given entry. Raises an error on duplicate keys.
insert (entry) {
let insertModifier = (t) => {
if (t.node) throw `This key already in tree: ${entry.key}`;
t.node = new BinaryTreeNode (entry, new AVLTree(), new AVLTree())
return true;
}
return this.findAndModify (entry.key, insertModifier)
}
// Finds a key and deletes it
findAndDelete (key) {
let deleteModifier = (t) => {
t.delete()
}
return this.findAndModify (key, deleteModifier)
}
// Deletes the root
delete () {
super.delete()
this.rebalance()
}
// Returns a shallow copy of this tree
clone() {
if (!this.node) return new AVLTree();
return new AVLTree (new BinaryTreeNode (this.node.entry,
this.node.left.clone(),
this.node.right.clone()))
}
}