avl
Version:
Fast AVL tree for Node and browser
595 lines (594 loc) • 14.9 kB
JavaScript
"use strict";
var __defProp = Object.defineProperty;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
/**
* avl v1.6.0
* Fast AVL tree for Node and browser
*
* @author Alexander Milevski <alex@milevski.co>
* @license MIT
* @preserve
*/
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
function print(root, printNode = (n) => n.key) {
const out = [];
row(root, "", true, (v) => out.push(v), printNode);
return out.join("");
}
function row(root, prefix, isTail, out, printNode) {
if (root) {
out(`${prefix}${isTail ? "└── " : "├── "}${printNode(root)}
`);
const indent = prefix + (isTail ? " " : "│ ");
if (root.left) row(root.left, indent, false, out, printNode);
if (root.right) row(root.right, indent, true, out, printNode);
}
}
function isBalanced(root) {
if (root === null) return true;
const lh = height(root.left);
const rh = height(root.right);
if (Math.abs(lh - rh) <= 1 && isBalanced(root.left) && isBalanced(root.right))
return true;
return false;
}
function height(node) {
return node ? 1 + Math.max(height(node.left), height(node.right)) : 0;
}
function loadRecursive(parent, keys, values, start, end) {
const size = end - start;
if (size > 0) {
const middle = start + Math.floor(size / 2);
const key = keys[middle];
const data = values[middle];
const node = { key, data, parent };
node.left = loadRecursive(node, keys, values, start, middle);
node.right = loadRecursive(node, keys, values, middle + 1, end);
return node;
}
return null;
}
function markBalance(node) {
if (node === null) return 0;
const lh = markBalance(node.left);
const rh = markBalance(node.right);
node.balanceFactor = lh - rh;
return Math.max(lh, rh) + 1;
}
function sort(keys, values, left, right, compare) {
if (left >= right) return;
const pivot = keys[left + right >> 1];
let i = left - 1;
let j = right + 1;
while (true) {
do
i++;
while (compare(keys[i], pivot) < 0);
do
j--;
while (compare(keys[j], pivot) > 0);
if (i >= j) break;
const tmpk = keys[i];
keys[i] = keys[j];
keys[j] = tmpk;
const tmpv = values[i];
values[i] = values[j];
values[j] = tmpv;
}
sort(keys, values, left, j, compare);
sort(keys, values, j + 1, right, compare);
}
function DEFAULT_COMPARE(a, b) {
return a > b ? 1 : a < b ? -1 : 0;
}
function rotateLeft(node) {
const rightNode = node.right;
node.right = rightNode.left;
if (rightNode.left) rightNode.left.parent = node;
rightNode.parent = node.parent;
if (rightNode.parent) {
if (rightNode.parent.left === node) {
rightNode.parent.left = rightNode;
} else {
rightNode.parent.right = rightNode;
}
}
node.parent = rightNode;
rightNode.left = node;
node.balanceFactor += 1;
if (rightNode.balanceFactor < 0) {
node.balanceFactor -= rightNode.balanceFactor;
}
rightNode.balanceFactor += 1;
if (node.balanceFactor > 0) {
rightNode.balanceFactor += node.balanceFactor;
}
return rightNode;
}
function rotateRight(node) {
const leftNode = node.left;
node.left = leftNode.right;
if (node.left) node.left.parent = node;
leftNode.parent = node.parent;
if (leftNode.parent) {
if (leftNode.parent.left === node) {
leftNode.parent.left = leftNode;
} else {
leftNode.parent.right = leftNode;
}
}
node.parent = leftNode;
leftNode.right = node;
node.balanceFactor -= 1;
if (leftNode.balanceFactor > 0) {
node.balanceFactor -= leftNode.balanceFactor;
}
leftNode.balanceFactor -= 1;
if (node.balanceFactor < 0) {
leftNode.balanceFactor += node.balanceFactor;
}
return leftNode;
}
class AVLTree {
constructor(comparator = DEFAULT_COMPARE, noDuplicates = false) {
__publicField(this, "_comparator");
__publicField(this, "_root");
__publicField(this, "_size");
__publicField(this, "_noDuplicates");
this._comparator = comparator || DEFAULT_COMPARE;
this._root = null;
this._size = 0;
this._noDuplicates = !!noDuplicates;
}
/**
* Clear the tree
*/
destroy() {
return this.clear();
}
/**
* Clear the tree
* @return {AVLTree}
*/
clear() {
this._root = null;
this._size = 0;
return this;
}
/**
* Number of nodes
* @return {number}
*/
get size() {
return this._size;
}
get root() {
return this._root;
}
/**
* Whether the tree contains a node with the given key
*/
contains(key) {
if (this._root) {
let node = this._root;
const comparator = this._comparator;
while (node) {
const cmp = comparator(key, node.key);
if (cmp === 0) return true;
else if (cmp < 0) node = node.left;
else node = node.right;
}
}
return false;
}
/**
* Successor node
*/
next(node) {
let successor = node;
if (successor) {
if (successor.right) {
successor = successor.right;
while (successor.left) successor = successor.left;
} else {
successor = node.parent;
while (successor && successor.right === node) {
node = successor;
successor = successor.parent;
}
}
}
return successor;
}
/**
* Predecessor node
*/
prev(node) {
let predecessor = node;
if (predecessor) {
if (predecessor.left) {
predecessor = predecessor.left;
while (predecessor.right) predecessor = predecessor.right;
} else {
predecessor = node.parent;
while (predecessor && predecessor.left === node) {
node = predecessor;
predecessor = predecessor.parent;
}
}
}
return predecessor;
}
/**
* @param {forEachCallback} callback
* @return {AVLTree}
*/
forEach(callback) {
let current = this._root;
const s = [];
let done = false;
let i = 0;
while (!done) {
if (current) {
s.push(current);
current = current.left;
} else {
if (s.length > 0) {
current = s.pop();
callback(current, i++);
current = current.right;
} else done = true;
}
}
return this;
}
/**
* Walk key range from `low` to `high`. Stops if `fn` returns a value.
* @param {Key} low
* @param {Key} high
* @param {Function} fn
* @param {*?} ctx
* @return {SplayTree}
*/
range(low, high, fn, ctx) {
const Q = [];
const compare = this._comparator;
let node = this._root;
while (Q.length !== 0 || node) {
if (node) {
Q.push(node);
node = node.left;
} else {
node = Q.pop();
const cmp = compare(node.key, high);
if (cmp > 0) break;
else if (compare(node.key, low) >= 0) {
if (fn.call(ctx, node)) return this;
}
node = node.right;
}
}
return this;
}
/**
* Returns all keys in order
*/
keys() {
let current = this._root;
const s = [];
const r = [];
let done = false;
while (!done) {
if (current) {
s.push(current);
current = current.left;
} else {
if (s.length > 0) {
current = s.pop();
r.push(current.key);
current = current.right;
} else done = true;
}
}
return r;
}
/**
* Returns `data` fields of all nodes in order.
*/
values() {
let current = this._root;
const s = [];
const r = [];
let done = false;
while (!done) {
if (current) {
s.push(current);
current = current.left;
} else {
if (s.length > 0) {
current = s.pop();
r.push(current.data);
current = current.right;
} else done = true;
}
}
return r;
}
/**
* Returns node at given index
*/
at(index) {
let current = this._root;
const s = [];
let done = false;
let i = 0;
while (!done) {
if (current) {
s.push(current);
current = current.left;
} else {
if (s.length > 0) {
current = s.pop();
if (i === index) return current;
i++;
current = current.right;
} else done = true;
}
}
return null;
}
/**
* Returns node with the minimum key
*/
minNode() {
let node = this._root;
if (!node) return null;
while (node.left) node = node.left;
return node;
}
/**
* Returns node with the max key
*/
maxNode() {
let node = this._root;
if (!node) return null;
while (node.right) node = node.right;
return node;
}
/**
* Min key
*/
min() {
let node = this._root;
if (!node) return null;
while (node.left) node = node.left;
return node.key;
}
/**
* Max key
*/
max() {
let node = this._root;
if (!node) return null;
while (node.right) node = node.right;
return node.key;
}
/**
* @return {boolean} true/false
*/
isEmpty() {
return this._root === null;
}
/**
* Removes and returns the node with smallest key
*/
pop() {
let node = this._root;
let returnValue = null;
if (node) {
while (node.left) node = node.left;
returnValue = { key: node.key, data: node.data };
this.remove(node.key);
}
return returnValue;
}
/**
* Removes and returns the node with highest key
*/
popMax() {
let node = this._root;
let returnValue = null;
if (node) {
while (node.right) node = node.right;
returnValue = { key: node.key, data: node.data };
this.remove(node.key);
}
return returnValue;
}
/**
* Find node by key
*/
find(key) {
const root = this._root;
let subtree = root;
const compare = this._comparator;
while (subtree) {
const cmp = compare(key, subtree.key);
if (cmp === 0) return subtree;
else if (cmp < 0) subtree = subtree.left;
else subtree = subtree.right;
}
return null;
}
/**
* Insert a node into the tree
*/
insert(key, data) {
if (!this._root) {
this._root = {
parent: null,
left: null,
right: null,
balanceFactor: 0,
key,
data
};
this._size++;
return this._root;
}
const compare = this._comparator;
let node = this._root;
let parent = null;
let cmp = 0;
if (this._noDuplicates) {
while (node) {
cmp = compare(key, node.key);
parent = node;
if (cmp === 0) return null;
else if (cmp < 0) node = node.left;
else node = node.right;
}
} else {
while (node) {
cmp = compare(key, node.key);
parent = node;
if (cmp <= 0) node = node.left;
else node = node.right;
}
}
const newNode = {
left: null,
right: null,
balanceFactor: 0,
parent,
key,
data
};
let newRoot = null;
if (cmp <= 0) parent.left = newNode;
else parent.right = newNode;
while (parent) {
cmp = compare(parent.key, key);
if (cmp < 0) parent.balanceFactor -= 1;
else parent.balanceFactor += 1;
if (parent.balanceFactor === 0) break;
else if (parent.balanceFactor < -1) {
if (parent.right.balanceFactor === 1) rotateRight(parent.right);
newRoot = rotateLeft(parent);
if (parent === this._root) this._root = newRoot;
break;
} else if (parent.balanceFactor > 1) {
if (parent.left.balanceFactor === -1) rotateLeft(parent.left);
newRoot = rotateRight(parent);
if (parent === this._root) this._root = newRoot;
break;
}
parent = parent.parent;
}
this._size++;
return newNode;
}
/**
* Removes the node from the tree. If not found, returns null.
*/
remove(key) {
if (!this._root) return null;
let node = this._root;
const compare = this._comparator;
let cmp = 0;
while (node) {
cmp = compare(key, node.key);
if (cmp === 0) break;
else if (cmp < 0) node = node.left;
else node = node.right;
}
if (!node) return null;
const returnValue = node.key;
let max, min;
if (node.left) {
max = node.left;
while (max.left || max.right) {
while (max.right) max = max.right;
node.key = max.key;
node.data = max.data;
if (max.left) {
node = max;
max = max.left;
}
}
node.key = max.key;
node.data = max.data;
node = max;
}
if (node.right) {
min = node.right;
while (min.left || min.right) {
while (min.left) min = min.left;
node.key = min.key;
node.data = min.data;
if (min.right) {
node = min;
min = min.right;
}
}
node.key = min.key;
node.data = min.data;
node = min;
}
let parent = node.parent;
let pp = node;
let newRoot;
while (parent) {
if (parent.left === pp) parent.balanceFactor -= 1;
else parent.balanceFactor += 1;
if (parent.balanceFactor < -1) {
if (parent.right.balanceFactor === 1) rotateRight(parent.right);
newRoot = rotateLeft(parent);
if (parent === this._root) this._root = newRoot;
parent = newRoot;
} else if (parent.balanceFactor > 1) {
if (parent.left.balanceFactor === -1) rotateLeft(parent.left);
newRoot = rotateRight(parent);
if (parent === this._root) this._root = newRoot;
parent = newRoot;
}
if (parent.balanceFactor === -1 || parent.balanceFactor === 1) break;
pp = parent;
parent = parent.parent;
}
if (node.parent) {
if (node.parent.left === node) node.parent.left = null;
else node.parent.right = null;
}
if (node === this._root) this._root = null;
this._size--;
return returnValue;
}
/**
* Bulk-load items
*/
load(keys = [], values = [], presort) {
if (this._size !== 0) throw new Error("bulk-load: tree is not empty");
const size = keys.length;
if (presort) sort(keys, values, 0, size - 1, this._comparator);
this._root = loadRecursive(null, keys, values, 0, size);
markBalance(this._root);
this._size = size;
return this;
}
/**
* Returns true if the tree is balanced
*/
isBalanced() {
return isBalanced(this._root);
}
/**
* String representation of the tree - primitive horizontal print-out
*/
toString(printNode) {
return print(this._root, printNode);
}
}
exports.AVLTree = AVLTree;
exports.Tree = AVLTree;