dastal
Version:
Data Structures & Algorithms implementations
219 lines • 7.15 kB
JavaScript
"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