dastal
Version:
Data Structures & Algorithms implementations
240 lines • 7.61 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.LevelOrderSegmentTree = void 0;
/**
* Thanks to [Douglas Wilhelm Harder](https://ece.uwaterloo.ca/~dwharder/aads/Algorithms/Array_resizing/)
* for their analysis on array resizing
*/
const arrayUtils_1 = require("src/utils/arrayUtils");
const collection_1 = require("src/collection");
const u32_1 = require("../math/u32");
const env_1 = require("src/env");
/**
* A {@link SegmentTree} with entries stored in level-order traversal.
*
* Memory usage: n elements require n - 1 + 2**(⌊log<sub>2</sub>(n-1)⌋ + 1) space.
*
*/
class LevelOrderSegmentTree {
/**
* Construct a new {@link SegmentTree}
*
* @param combinFn - The function used to aggregate elements
* @param elements - Initial elements to build into the tree
*/
constructor(combine, elements = []) {
this.array = [];
this.combine = combine;
this.length = 0;
this.level = 0;
this.build(elements);
}
clear() {
this.length = 0;
this.level = 0;
this.array.length = 0;
}
pop() {
// Sanitize range
if (this.length <= this.level) {
return undefined;
}
// Remove element
const out = this.array[--this.length];
// If level is <= 1/4 full
if (this.size <= (this.level + 1) >>> 2) {
this.shrink();
}
return out;
}
push(element) {
// If array is full
if (this.length >= this.array.length) {
this.grow();
}
// Add the new element
this.array[this.length++] = element;
// Update aggregation nodes
for (let i = this.length; i & 1; this.array[i - 1] = element) {
element = this.combine(this.array[i - 2], element);
i >>>= 1;
}
return this.size;
}
query(min, max) {
// Sanitize range
if (min >= max) {
throw new RangeError(`Range [${min}..${max}) is empty`);
}
if (min < 0 || max > this.size) {
throw new RangeError(`Range [${min}..${max}) not in [0..${this.size})`);
}
// Translate range to interior indices and align with powers of 2
min += this.level + 1;
max += this.level + 1;
// Take the longest possible jump from min
let offset = u32_1.lsp(min | u32_1.msp(max - min));
let value = this.array[min / offset - 1];
min += offset;
// Continue jumping until max
while (min < max) {
offset = u32_1.lsp(min | u32_1.msp(max - min));
value = this.combine(value, this.array[min / offset - 1]);
min += offset;
}
return value;
}
get size() {
return this.length - this.level;
}
/**
* Return an iterator through the elements
*/
*[Symbol.iterator]() {
for (let i = 0; i < this.size; ++i) {
yield this.array[this.level + i];
}
}
update(min, max, operation) {
// Sanitize range
if (min >= max) {
return;
}
if (min < 0 || max > this.size) {
throw new RangeError(`Range [${min}..${max}) not in [0..${this.size})`);
}
// Translate range to interior indices
min += this.level;
max += this.level;
// Update the range
for (let i = min; i < max; ++i) {
this.array[i] = operation(this.array[i], i - this.level);
}
// Update the range's aggregation nodes
this.aggregate(min, max);
}
/**
* A helper method to aggregate a range of elements
*/
aggregate(min, max) {
// Align indices with powers of 2
++min;
++max;
// Aggregate elements
for (let cap = this.length + 1; min < max; cap >>>= 1) {
max += max & ((max - cap) >>> 31);
for (let i = (min | 1) >>> 0; i < max; i += 2) {
this.array[(i >>> 1) - 1] = this.combine(this.array[i - 2], this.array[i - 1]);
}
min >>>= 1;
max >>>= 1;
}
}
/**
* A helper method used to build the tree
*
* @param elements The initial set of elements to add into the tree
*/
build(elements) {
let key = undefined;
// Check if the iterable's size can be known.
if (arrayUtils_1.isArray(elements)) {
key = 'length';
}
else if (collection_1.isCollection(elements)) {
key = 'size';
}
else {
for (const element of elements) {
this.push(element);
}
return;
}
// Get the iterable's size
const n = elements[key];
// Check for base case
if (n < 2) {
this.level = 0;
this.length = n;
this.array.length = 0;
this.array.push(...elements);
return;
}
// Check if max capacity reached
const level = 2 * u32_1.msp(n - 1) - 1;
if (level + n > env_1.MAX_ARRAY_LENGTH) {
throw new RangeError('Invalid length');
}
// Allocate the array
this.level = level;
this.length = level;
this.array.length = Math.min(2 * level + 1, env_1.MAX_ARRAY_LENGTH);
// Add the elements
for (const element of elements) {
this.array[this.length++] = element;
}
// Update aggregation nodes
this.aggregate(this.level, this.length);
}
/**
* Shift the tree down a level
*/
grow() {
// Check if max capacity reached
const level = 2 * this.level + 1;
if (level + this.size + 1 > env_1.MAX_ARRAY_LENGTH) {
throw new RangeError('Invalid length');
}
// Check base case
if (this.length < 1) {
this.array.length = 1;
return;
}
// Extend capacity
this.array.length = Math.min(2 * level + 1, env_1.MAX_ARRAY_LENGTH);
// Shift the tree down a level
let min = this.level + 1;
for (let max = this.length + 1; min < max; max >>>= 1) {
this.array.copyWithin(2 * min - 1, min - 1, max - 1);
min >>>= 1;
}
// Update pointers
this.length += this.level + 1;
this.level += this.level + 1;
}
/**
* Shift the tree to the highest non-full level
*/
shrink() {
const length = this.length - this.level;
// Check base case
if (length < 2) {
this.array.copyWithin(0, this.level, this.length);
this.level = 0;
this.length = length;
this.array.length = length;
return;
}
// Get the highest node
let min = this.level + 1;
let mask = u32_1.msp(length);
min = min / u32_1.lsp(min | mask) - 1;
// Check if shrinking is possible
if (min < 2) {
return;
}
// Update the tree
this.level = 0;
for (let max = min + 1; mask; min += min + 1) {
this.level += this.level + 1;
this.array.copyWithin(this.level, min, max);
mask >>>= 1;
max += max + 2 + +((length & mask) > 0);
}
// Update pointers
this.length = this.level + length;
this.array.length = 2 * this.level + 1;
}
}
exports.LevelOrderSegmentTree = LevelOrderSegmentTree;
//# sourceMappingURL=levelOrderSegmentTree.js.map