UNPKG

@ickb/utils

Version:

General utilities built on top of CCC

176 lines (161 loc) 5.2 kB
/** * A class representing a minimum heap data structure. * @template T - The type of elements in the heap. * * @credits go standard library authors, this implementation is just a translation: * https://go.dev/src/container/heap/heap.go */ export class MinHeap<T> { /** * Creates an instance of MinHeap. * @param {(heap: T[], i: number, j: number) => boolean} lessFunc - A function that evaluates if element at index i is less than element at index j. * @param {T[]} heap - The initial array of elements in the heap. */ constructor( public readonly lessFunc: (heap: T[], i: number, j: number) => boolean, public readonly heap: T[], ) {} /** * Initializes the heap from an array of elements. * @param {T[]} unordered - The array of unordered elements to create the heap from. * @param {(heap: T[], i: number, j: number) => boolean} lessFunc - A function that evaluates if element at index i is less than element at index j. * @returns {MinHeap<T>} A new instance of MinHeap. */ static from<T>( lessFunc: (heap: T[], i: number, j: number) => boolean, unordered: T[] = [], ): MinHeap<T> { const heap = new MinHeap<T>(lessFunc, [...unordered]); const n = heap.len(); for (let i = (n >> 1) - 1; i >= 0; i--) { heap.down(i, n); } return heap; } /** * Evaluates if the element at index i is less than the element at index j using the external lessFunc. * @param {number} i - The index of the first element. * @param {number} j - The index of the second element. * @returns {boolean} True if the element at index i is less than the element at index j; otherwise, false. */ private less(i: number, j: number): boolean { return this.lessFunc(this.heap, i, j); } /** * Returns the number of elements in the heap. * @returns {number} The number of elements in the heap. */ len(): number { return this.heap.length; } /** * Returns true iff the number of elements in the heap is zero. * @returns {number} the emptiness of the array. */ isEmpty(): boolean { return this.heap.length === 0; } /** * Adds an element x to the heap. * @param {T} x - The element to be added to the heap. */ push(x: T): void { this.heap.push(x); this.up(this.len() - 1); } /** * Removes and returns the minimum element from the heap. * @returns {T | undefined} The minimum element from the heap, or undefined if the heap is empty. */ pop(): T | undefined { if (this.len() === 0) { return; } const n = this.len() - 1; this.swap(0, n); this.down(0, n); return this.heap.pop(); } /** * Removes and returns the element at index i from the heap. * @param {number} i - The index of the element to be removed. * @returns {T | undefined} The removed element, or undefined if the index is out of bounds. */ remove(i: number): T | undefined { if (this.len() <= i) { return; } const n = this.len() - 1; if (n != i) { this.swap(i, n); if (!this.down(i, n)) { this.up(i); } } return this.pop(); } /** * Re-establishes the heap ordering after the element at index i has changed its value. * @param {number} i - The index of the element to fix. */ fix(i: number): void { if (this.len() <= i) { return; } if (!this.down(i, this.len())) { this.up(i); } } /** * Moves the element at index j up the heap to restore the heap property. * @param {number} j - The index of the element to move up. */ private up(j: number): void { // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition while (true) { const i = (j - 1) >> 1; // parent if (i == j || !this.less(j, i)) { break; } this.swap(i, j); j = i; } } /** * Moves the element at index i down the heap to restore the heap property. * @param {number} i0 - The index of the element to move down. * @param {number} n - The number of elements in the heap. * @returns {boolean} True if the element was moved down; otherwise, false. */ private down(i0: number, n: number): boolean { let i = i0; // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition while (true) { const j1 = (i << 1) + 1; // left child index if (j1 >= n || j1 < 0) { // j1 < 0 after int overflow break; } let j = j1; // left child const j2 = j1 + 1; // right child index if (j2 < n && this.less(j2, j1)) { j = j2; // choose the smaller child } if (!this.less(j, i)) { break; } this.swap(i, j); i = j; } return i > i0; // returns true if the position of the element has changed } /** * Swaps the elements at indices i and j in the heap. * @param {number} i - The index of the first element. * @param {number} j - The index of the second element. */ private swap(i: number, j: number): void { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion [this.heap[i], this.heap[j]] = [this.heap[j]!, this.heap[i]!]; } }