UNPKG

dastal

Version:

Data Structures & Algorithms implementations

224 lines 7.04 kB
"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