doubly-linked-list-typed
Version:
Doubly Linked List
332 lines (293 loc) • 11.3 kB
text/typescript
/**
* data-structure-typed
*
* @author Pablo Zeng
* @copyright Copyright (c) 2022 Pablo Zeng <zrwusa@gmail.com>
* @license MIT License
*/
import { getMSB } from '../../utils';
/**
*
*/
export class BinaryIndexedTree {
protected readonly _freq: number;
protected readonly _max: number;
/**
* The constructor initializes the properties of an object, including default frequency, maximum
* value, a freqMap data structure, the most significant bit, and the count of negative frequencies.
* @param - - `frequency`: The default frequency value. It is optional and has a default
* value of 0.
*/
constructor({ frequency = 0, max }: { frequency?: number; max: number }) {
this._freq = frequency;
this._max = max;
this._freqMap = { 0: 0 };
this._msb = getMSB(max);
this._negativeCount = frequency < 0 ? max : 0;
}
protected _freqMap: Record<number, number>;
/**
* The function returns the frequency map of numbers.
* @returns The `_freqMap` property, which is a record with number keys and number values, is being
* returned.
*/
get freqMap(): Record<number, number> {
return this._freqMap;
}
protected _msb: number;
/**
* The function returns the value of the _msb property.
* @returns The `_msb` property of the object.
*/
get msb(): number {
return this._msb;
}
protected _negativeCount: number;
/**
* The function returns the value of the _negativeCount property.
* @returns The method is returning the value of the variable `_negativeCount`, which is of type
* `number`.
*/
get negativeCount(): number {
return this._negativeCount;
}
/**
* The above function returns the value of the protected variable `_freq`.
* @returns The frequency value stored in the protected variable `_freq`.
*/
get freq(): number {
return this._freq;
}
/**
* The above function returns the maximum value.
* @returns The maximum value stored in the variable `_max`.
*/
get max(): number {
return this._max;
}
/**
* The function "readSingle" reads a single number from a specified index.
* @param {number} index - The `index` parameter is a number that represents the index of an element in a
* collection or array.
* @returns a number.
*/
readSingle(index: number): number {
this._checkIndex(index);
return this._readSingle(index);
}
/**
* The "update" function updates the value at a given index by adding a delta and triggers a callback
* to notify of the change.
* @param {number} position - The `index` parameter represents the index of the element that needs to be
* updated in the data structure.
* @param {number} change - The "delta" parameter represents the change in value that needs to be
* applied to the frequency at the specified index.
*/
update(position: number, change: number): void {
this._checkIndex(position);
const freqCur = this._readSingle(position);
this._update(position, change);
this._updateNegativeCount(freqCur, freqCur + change);
}
/**
* The function "writeSingle" checks the index and writes a single value with a given frequency.
* @param {number} index - The `index` parameter is a number that represents the index of an element. It
* is used to identify the specific element that needs to be written.
* @param {number} freq - The `freq` parameter represents the frequency value that needs to be
* written.
*/
writeSingle(index: number, freq: number): void {
this._checkIndex(index);
this._writeSingle(index, freq);
}
/**
* The read function takes a count parameter, checks if it is an integer, and returns the result of
* calling the _read function with the count parameter clamped between 0 and the maximum value.
* @param {number} count - The `count` parameter is a number that represents the number of items to
* read.
* @returns a number.
*/
read(count: number): number {
if (!Number.isInteger(count)) {
throw new Error('Invalid count');
}
return this._read(Math.max(Math.min(count, this.max), 0));
}
/**
* The function returns the lower bound of a non-descending sequence that sums up to a given number.
* @param {number} sum - The `sum` parameter is a number that represents the target sum that we want
* to find in the sequence.
* @returns The lowerBound function is returning a number.
*/
lowerBound(sum: number): number {
if (this.negativeCount > 0) {
throw new Error('Sequence is not non-descending');
}
return this._binarySearch(sum, (x, y) => x < y);
}
/**
* The upperBound function returns the index of the first element in a sequence that is greater than
* or equal to a given sum.
* @param {number} sum - The "sum" parameter is a number that represents the target sum that we want
* to find in the sequence.
* @returns The upperBound function is returning a number.
*/
upperBound(sum: number): number {
if (this.negativeCount > 0) {
throw new Error('Must not be descending');
}
return this._binarySearch(sum, (x, y) => x <= y);
}
/**
* The function calculates the prefix sum of an array using a binary indexed tree.
* @param {number} i - The parameter "i" in the function "getPrefixSum" represents the index of the element in the
* array for which we want to calculate the prefix sum.
* @returns The function `getPrefixSum` returns the prefix sum of the elements in the binary indexed tree up to index
* `i`.
*/
getPrefixSum(i: number): number {
this._checkIndex(i);
i++; // Convert to 1-based index
let sum = 0;
while (i > 0) {
sum += this._getFrequency(i);
i -= i & -i;
}
return sum;
}
/**
* The function returns the value of a specific index in a freqMap data structure, or a default value if
* the index is not found.
* @param {number} index - The `index` parameter is a number that represents the index of a node in a
* freqMap data structure.
* @returns a number.
*/
protected _getFrequency(index: number): number {
if (index in this.freqMap) {
return this.freqMap[index];
}
return this.freq * (index & -index);
}
/**
* The function _updateFrequency adds a delta value to the element at the specified index in the freqMap array.
* @param {number} index - The index parameter is a number that represents the index of the freqMap
* element that needs to be updated.
* @param {number} delta - The `delta` parameter represents the change in value that needs to be
* added to the freqMap at the specified `index`.
*/
protected _updateFrequency(index: number, delta: number): void {
this.freqMap[index] = this._getFrequency(index) + delta;
}
/**
* The function checks if the given index is valid and within the range.
* @param {number} index - The parameter "index" is of type number and represents the index value
* that needs to be checked.
*/
protected _checkIndex(index: number): void {
if (!Number.isInteger(index)) {
throw new Error('Invalid index: Index must be an integer.');
}
if (index < 0 || index >= this.max) {
throw new Error('Index out of range: Index must be within the range [0, this.max).');
}
}
/**
* The function calculates the sum of elements in an array up to a given index using a binary indexed
* freqMap.
* @param {number} index - The `index` parameter is a number that represents the index of an element in a
* data structure.
* @returns a number.
*/
protected _readSingle(index: number): number {
index = index + 1;
let sum = this._getFrequency(index);
const z = index - (index & -index);
index--;
while (index !== z) {
sum -= this._getFrequency(index);
index -= index & -index;
}
return sum;
}
/**
* The function `_updateNegativeCount` updates a counter based on changes in frequency values.
* @param {number} freqCur - The current frequency value.
* @param {number} freqNew - The freqNew parameter represents the new frequency value.
*/
protected _updateNegativeCount(freqCur: number, freqNew: number): void {
if (freqCur < 0 && freqNew >= 0) {
this._negativeCount--;
} else if (freqCur >= 0 && freqNew < 0) {
this._negativeCount++;
}
}
/**
* The `_update` function updates the values in a binary indexed freqMap starting from a given index and
* propagating the changes to its parent nodes.
* @param {number} index - The `index` parameter is a number that represents the index of the element in
* the data structure that needs to be updated.
* @param {number} delta - The `delta` parameter represents the change in value that needs to be
* applied to the elements in the data structure.
*/
protected _update(index: number, delta: number): void {
index = index + 1;
while (index <= this.max) {
this._updateFrequency(index, delta);
index += index & -index;
}
}
/**
* The `_writeSingle` function updates the frequency at a specific index and triggers a callback if
* the frequency has changed.
* @param {number} index - The `index` parameter is a number that represents the index of the element
* being modified or accessed.
* @param {number} freq - The `freq` parameter represents the new frequency value that needs to be
* written to the specified index `index`.
*/
protected _writeSingle(index: number, freq: number): void {
const freqCur = this._readSingle(index);
this._update(index, freq - freqCur);
this._updateNegativeCount(freqCur, freq);
}
/**
* The `_read` function calculates the sum of values in a binary freqMap up to a given count.
* @param {number} count - The `count` parameter is a number that represents the number of elements
* to read from the freqMap.
* @returns the sum of the values obtained from calling the `_getFrequency` method for each index in the
* range from `count` to 1.
*/
protected _read(count: number): number {
let index = count;
let sum = 0;
while (index) {
sum += this._getFrequency(index);
index -= index & -index;
}
return sum;
}
/**
* The function `_binarySearch` performs a binary search to find the largest number that satisfies a given
* condition.
* @param {number} sum - The sum parameter is a number that represents the target sum value.
* @param before - The `before` parameter is a function that takes two numbers `x` and `y` as
* arguments and returns a boolean value. It is used to determine if `x` is less than or equal to
* `y`. The purpose of this function is to compare two numbers and determine their order.
* @returns the value of the variable "left".
*/
protected _binarySearch(sum: number, before: (x: number, y: number) => boolean): number {
let left = 0;
let right = this.msb << 1;
let sumT = sum;
while (right > left + 1) {
const middle = (left + right) >> 1;
const sumM = this._getFrequency(middle);
if (middle <= this.max && before(sumM, sumT)) {
sumT -= sumM;
left = middle;
} else {
right = middle;
}
}
return left;
}
}