dastal
Version:
Data Structures & Algorithms implementations
224 lines • 7.04 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.rotateR = exports.rotateL = exports.remove = exports.balance = exports.AVLTree = void 0;
const binaryTreeUtils_1 = require("./binaryTreeUtils");
const arrayUtils_1 = require("src/utils/arrayUtils");
/**
* An AVL tree is a self-balancing binary search tree ([source](https://en.wikipedia.org/wiki/AVL_tree)).
*
* It is named after inventors Georgy Adelson-Velsky and Evgenii Landis and was the first such
* data structure to be invented. In an AVL tree, the heights of the two child
* subtrees of any node differ by at most one; if at any time they differ by more
* than one, rebalancing is done to restore this property.
*
* Lookup, insertion, and deletion all take O(log(n)) time in both the average and worst cases,
* where n is the number of nodes in the tree prior to the operation. Insertions and deletions
* may require the tree to be rebalanced by one or more tree rotations.
*
* AVL trees are often compared with red–black trees as both take O(log(n))
* time for the basic operations. For lookup-intensive applications, AVL trees are
* faster than red–black trees because they are more strictly balanced.
* Similar to red–black trees, AVL trees are height-balanced.
*/
class AVLTree {
constructor(compareFn, allowDuplicates, elements) {
if (typeof allowDuplicates !== 'boolean') {
elements = allowDuplicates;
allowDuplicates = true;
}
this.compare = compareFn;
this.dupeWeight = +allowDuplicates;
this.length = 0;
this.root = {};
this.build(elements ?? []);
}
add(element) {
// Find the element
let edge = { from: this.root, label: 'left', to: this.root.left };
let stack = binaryTreeUtils_1.searchStack(element, { value: edge }, this.compare, this.dupeWeight);
// If element already exists
if (stack.value.to != null) {
return this;
}
// Add element
edge = stack.value;
let label = edge.label;
edge.from[label] = { balanceFactor: 0, value: element };
// Balance the tree
while (stack.next) {
stack = stack.next;
edge = stack.value;
edge.to.balanceFactor += label === 'left' ? -1 : 1;
edge.to = balance(edge.to);
edge.from[(label = edge.label)] = edge.to;
if (edge.to.balanceFactor === 0) {
break;
}
}
// Update state
++this.length;
return this;
}
clear() {
this.root.left = undefined;
this.length = 0;
}
comparator() {
return this.compare;
}
delete(element) {
// Remove the element if found
const edge = { from: this.root, label: 'left', to: this.root.left };
const stack = binaryTreeUtils_1.searchStack(element, { value: edge }, this.compare, 0);
const removed = remove(stack);
// Update state
this.length -= +removed;
return removed;
}
has(element) {
return binaryTreeUtils_1.search(element, this.root.left, this.compare) != null;
}
max() {
return binaryTreeUtils_1.rightmost(this.root.left)?.value;
}
min() {
return binaryTreeUtils_1.leftmost(this.root.left)?.value;
}
pop() {
// Find the maximum value
const edge = { from: this.root, label: 'left', to: this.root.left };
const stack = binaryTreeUtils_1.rightmostStack({ value: edge });
const value = stack.value.to?.value;
// Remove the value
const removed = remove(stack);
// Update state
this.length -= +removed;
return value;
}
shift() {
// Find the minimum value
const edge = { from: this.root, label: 'left', to: this.root.left };
const stack = binaryTreeUtils_1.leftmostStack({ value: edge });
const value = stack.value.to?.value;
// Remove the value
const removed = remove(stack);
// Update state
this.length -= +removed;
return value;
}
get size() {
return this.length;
}
*sorted() {
for (const node of binaryTreeUtils_1.inOrderTraverse(this.root.left)) {
yield node.value;
}
}
/**
* Receive an iterator through the list.
*
* **Note:** Unexpected behavior can occur if the collection is modified during iteration.
*
* @returns An iterator through the list
*/
*[Symbol.iterator]() {
for (const node of binaryTreeUtils_1.preOrderTraverse(this.root.left)) {
yield node.value;
}
}
update(curElement, newElement) {
if (this.delete(curElement)) {
this.add(newElement);
return true;
}
return false;
}
build(obj) {
if (arrayUtils_1.isArray(obj)) {
for (let i = 0; i < obj.length; ++i) {
this.add(obj[i]);
}
}
else if (obj instanceof AVLTree && this.compare === obj.compare) {
this.root = binaryTreeUtils_1.clone(obj.root);
this.length = obj.size;
}
else {
for (const element of obj) {
this.add(element);
}
}
}
}
exports.AVLTree = AVLTree;
/**
* @internal
*/
function balance(node) {
if (node.balanceFactor > 1) {
if (node.right.balanceFactor < 0) {
node.right = rotateR(node.right);
}
node = rotateL(node);
}
else if (node.balanceFactor < -1) {
if (node.left.balanceFactor > 0) {
node.left = rotateL(node.left);
}
node = rotateR(node);
}
return node;
}
exports.balance = balance;
/**
* @internal
*/
function remove(stack) {
let edge = stack.value;
const node = edge.to;
// If not found
if (node == null) {
return false;
}
// Remove the node
stack = binaryTreeUtils_1.removeStack(stack);
// Balance the tree
let label = stack.value.label;
while (stack.next) {
stack = stack.next;
edge = stack.value;
edge.to.balanceFactor -= label === 'left' ? -1 : 1;
edge.to = balance(edge.to);
edge.from[(label = edge.label)] = edge.to;
if (edge.to.balanceFactor !== 0) {
break;
}
}
return true;
}
exports.remove = remove;
/**
* @internal
*/
function rotateL(P) {
const R = P.right;
P.right = R.left;
R.left = P;
P.balanceFactor -= 1 + Math.max(0, R.balanceFactor);
R.balanceFactor -= 1 - Math.min(0, P.balanceFactor);
return R;
}
exports.rotateL = rotateL;
/**
* @internal
*/
function rotateR(P) {
const L = P.left;
P.left = L.right;
L.right = P;
P.balanceFactor += 1 - Math.min(0, L.balanceFactor);
L.balanceFactor += 1 + Math.max(0, P.balanceFactor);
return L;
}
exports.rotateR = rotateR;
//# sourceMappingURL=avlTree.js.map