UNPKG

dastal

Version:

Data Structures & Algorithms implementations

219 lines 7.15 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.split = exports.skew = exports.remove = exports.AATree = void 0; const binaryTreeUtils_1 = require("./binaryTreeUtils"); const arrayUtils_1 = require("src/utils/arrayUtils"); /** * An AA tree is a form of balanced tree used for storing and retrieving ordered data efficiently * ([source](https://en.wikipedia.org/wiki/AA_tree)). * * AA trees are named for Arne Andersson, their inventor. They are a variation of the red–black tree, * which supports efficient addition and deletion of entries. Unlike red–black trees, additional * constraints on the balancing mechanism greatly simplifies the implementation as well as * maintenance operations; While a red–black tree needs to consider seven different shapes * to properly balance the tree, an AA tree only needs to consider two shapes. * * The performance of an AA tree is equivalent to the performance of a red–black tree. * While an AA tree makes more rotations than a red-black tree, the simpler algorithms * tend to be faster, which balances out to similar performance. A red-black tree is * more consistent in its performance, but an AA tree tends to be flatter, which results * in slightly faster search times. */ class AATree { constructor(compareFn, allowDuplicates, elements) { if (typeof allowDuplicates !== 'boolean') { elements = allowDuplicates; allowDuplicates = true; } this.compare = compareFn; this.dupeWeight = +allowDuplicates; this.length = 0; this.build(elements ?? []); } add(element) { // Find the element const sentinel = { left: this.root }; let edge = { from: sentinel, label: 'left', to: this.root }; 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] = { level: 1, value: element }; // Balance the tree while (stack.next) { stack = stack.next; edge = stack.value; edge.to = split(skew(edge.to)); edge.from[(label = edge.label)] = edge.to; } // Update state ++this.length; this.root = sentinel.left; return this; } clear() { this.root = undefined; this.length = 0; } comparator() { return this.compare; } delete(element) { // Remove the element if found const sentinel = { left: this.root }; const edge = { from: sentinel, label: 'left', to: this.root }; const stack = binaryTreeUtils_1.searchStack(element, { value: edge }, this.compare, 0); const removed = remove(stack); // Update state this.root = sentinel.left; this.length -= +removed; return removed; } has(element) { return binaryTreeUtils_1.search(element, this.root, this.compare) != null; } max() { return binaryTreeUtils_1.rightmost(this.root)?.value; } min() { return binaryTreeUtils_1.leftmost(this.root)?.value; } pop() { // Find the maximum value const sentinel = { left: this.root }; const edge = { from: sentinel, label: 'left', to: this.root }; const stack = binaryTreeUtils_1.rightmostStack({ value: edge }); const value = stack.value.to?.value; // Remove the value const removed = remove(stack); // Update state this.root = sentinel.left; this.length -= +removed; return value; } shift() { // Find the minimum value const sentinel = { left: this.root }; const edge = { from: sentinel, label: 'left', to: this.root }; const stack = binaryTreeUtils_1.leftmostStack({ value: edge }); const value = stack.value.to?.value; // Remove the value const removed = remove(stack); // Update state this.root = sentinel.left; this.length -= +removed; return value; } get size() { return this.length; } *sorted() { for (const node of binaryTreeUtils_1.inOrderTraverse(this.root)) { 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)) { 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 AATree && 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.AATree = AATree; /** * @internal */ function remove(stack) { let edge = stack.value; let node = edge.to; // If not found if (node == null) { return false; } // Remove the node stack = binaryTreeUtils_1.removeStack(stack); // Update the tree while (stack.next) { stack = stack.next; edge = stack.value; node = edge.to; // Decrease levels const level = 1 + Math.min(node.left?.level ?? 0, node.right?.level ?? 0); if (level < node.level) { node.level = level; if (node.right != null && level < node.right.level) { node.right.level = level; } } // Balance node = skew(node); node.right = skew(node.right); if (node.right != null) { node.right.right = skew(node.right.right); } node = split(node); node.right = split(node.right); // Make the update edge.from[edge.label] = edge.to = node; } return true; } exports.remove = remove; function skew(node) { if (node == null || node.left == null || node.level != node.left.level) { return node; } const left = node.left; node.left = left.right; left.right = node; return left; } exports.skew = skew; function split(node) { if (node == null || node.right == null || node.right.right == null || node.level != node.right.right.level) { return node; } const right = node.right; node.right = right.left; right.left = node; ++right.level; return right; } exports.split = split; //# sourceMappingURL=aaTree.js.map