UNPKG

tree-multimap-typed

Version:
504 lines (503 loc) 19.1 kB
/** * data-structure-typed * * @author Pablo Zeng * @copyright Copyright (c) 2022 Pablo Zeng <zrwusa@gmail.com> * @license MIT License */ import type { Comparator, DFSOrderPattern, ElementCallback, HeapOptions } from '../../types'; import { IterableElementBase } from '../base'; /** * Binary heap with pluggable comparator; supports fast insertion and removal of the top element. * @remarks Time O(1), Space O(1) * @template E * @template R * 1. Complete Binary Tree: Heaps are typically complete binary trees, meaning every level is fully filled except possibly for the last level, which has nodes as far left as possible. * 2. Heap Properties: Each node in a heap follows a specific order property, which varies depending on the type of heap: * Max Heap: The value of each parent node is greater than or equal to the value of its children. * Min Heap: The value of each parent node is less than or equal to the value of its children. * 3. Root Node Access: In a heap, the largest element (in a max heap) or the smallest element (in a min heap) is always at the root of the tree. * 4. Efficient Insertion and Deletion: Due to its structure, a heap allows for insertion and deletion operations in logarithmic time (O(log n)). * 5. Managing Dynamic Data Sets: Heaps effectively manage dynamic data sets, especially when frequent access to the largest or smallest elements is required. * 6. Non-linear Search: While a heap allows rapid access to its largest or smallest element, it is less efficient for other operations, such as searching for a specific element, as it is not designed for these tasks. * 7. Efficient Sorting Algorithms: For example, heap sort. Heap sort uses the properties of a heap to sort elements. * 8. Graph Algorithms: Such as Dijkstra's shortest path algorithm and Prime's minimum-spanning tree algorithm, which use heaps to improve performance. * @example * // Use Heap to sort an array * function heapSort(arr: number[]): number[] { * const heap = new Heap<number>(arr, { comparator: (a, b) => a - b }); * const sorted: number[] = []; * while (!heap.isEmpty()) { * sorted.push(heap.poll()!); // Poll minimum element * } * return sorted; * } * * const array = [5, 3, 8, 4, 1, 2]; * console.log(heapSort(array)); // [1, 2, 3, 4, 5, 8] * @example * // Use Heap to solve top k problems * function topKElements(arr: number[], k: number): number[] { * const heap = new Heap<number>([], { comparator: (a, b) => b - a }); // Max heap * arr.forEach(num => { * heap.add(num); * if (heap.size > k) heap.poll(); // Keep the heap size at K * }); * return heap.toArray(); * } * * const numbers = [10, 30, 20, 5, 15, 25]; * console.log(topKElements(numbers, 3)); // [15, 10, 5] * @example * // Use Heap to merge sorted sequences * function mergeSortedSequences(sequences: number[][]): number[] { * const heap = new Heap<{ value: number; seqIndex: number; itemIndex: number }>([], { * comparator: (a, b) => a.value - b.value // Min heap * }); * * // Initialize heap * sequences.forEach((seq, seqIndex) => { * if (seq.length) { * heap.add({ value: seq[0], seqIndex, itemIndex: 0 }); * } * }); * * const merged: number[] = []; * while (!heap.isEmpty()) { * const { value, seqIndex, itemIndex } = heap.poll()!; * merged.push(value); * * if (itemIndex + 1 < sequences[seqIndex].length) { * heap.add({ * value: sequences[seqIndex][itemIndex + 1], * seqIndex, * itemIndex: itemIndex + 1 * }); * } * } * * return merged; * } * * const sequences = [ * [1, 4, 7], * [2, 5, 8], * [3, 6, 9] * ]; * console.log(mergeSortedSequences(sequences)); // [1, 2, 3, 4, 5, 6, 7, 8, 9] * @example * // Use Heap to dynamically maintain the median * class MedianFinder { * private low: MaxHeap<number>; // Max heap, stores the smaller half * private high: MinHeap<number>; // Min heap, stores the larger half * * constructor() { * this.low = new MaxHeap<number>([]); * this.high = new MinHeap<number>([]); * } * * addNum(num: number): void { * if (this.low.isEmpty() || num <= this.low.peek()!) this.low.add(num); * else this.high.add(num); * * // Balance heaps * if (this.low.size > this.high.size + 1) this.high.add(this.low.poll()!); * else if (this.high.size > this.low.size) this.low.add(this.high.poll()!); * } * * findMedian(): number { * if (this.low.size === this.high.size) return (this.low.peek()! + this.high.peek()!) / 2; * return this.low.peek()!; * } * } * * const medianFinder = new MedianFinder(); * medianFinder.addNum(10); * console.log(medianFinder.findMedian()); // 10 * medianFinder.addNum(20); * console.log(medianFinder.findMedian()); // 15 * medianFinder.addNum(30); * console.log(medianFinder.findMedian()); // 20 * medianFinder.addNum(40); * console.log(medianFinder.findMedian()); // 25 * medianFinder.addNum(50); * console.log(medianFinder.findMedian()); // 30 * @example * // Use Heap for load balancing * function loadBalance(requests: number[], servers: number): number[] { * const serverHeap = new Heap<{ id: number; load: number }>([], { comparator: (a, b) => a.load - b.load }); // min heap * const serverLoads = new Array(servers).fill(0); * * for (let i = 0; i < servers; i++) { * serverHeap.add({ id: i, load: 0 }); * } * * requests.forEach(req => { * const server = serverHeap.poll()!; * serverLoads[server.id] += req; * server.load += req; * serverHeap.add(server); // The server after updating the load is re-entered into the heap * }); * * return serverLoads; * } * * const requests = [5, 2, 8, 3, 7]; * console.log(loadBalance(requests, 3)); // [12, 8, 5] * @example * // Use Heap to schedule tasks * type Task = [string, number]; * * function scheduleTasks(tasks: Task[], machines: number): Map<number, Task[]> { * const machineHeap = new Heap<{ id: number; load: number }>([], { comparator: (a, b) => a.load - b.load }); // Min heap * const allocation = new Map<number, Task[]>(); * * // Initialize the load on each machine * for (let i = 0; i < machines; i++) { * machineHeap.add({ id: i, load: 0 }); * allocation.set(i, []); * } * * // Assign tasks * tasks.forEach(([task, load]) => { * const machine = machineHeap.poll()!; * allocation.get(machine.id)!.push([task, load]); * machine.load += load; * machineHeap.add(machine); // The machine after updating the load is re-entered into the heap * }); * * return allocation; * } * * const tasks: Task[] = [ * ['Task1', 3], * ['Task2', 1], * ['Task3', 2], * ['Task4', 5], * ['Task5', 4] * ]; * const expectedMap = new Map<number, Task[]>(); * expectedMap.set(0, [ * ['Task1', 3], * ['Task4', 5] * ]); * expectedMap.set(1, [ * ['Task2', 1], * ['Task3', 2], * ['Task5', 4] * ]); * console.log(scheduleTasks(tasks, 2)); // expectedMap */ export declare class Heap<E = unknown, R = never> extends IterableElementBase<E, R> { protected _equals: (a: E, b: E) => boolean; /** * Create a Heap and optionally bulk-insert elements. * @remarks Time O(N), Space O(N) * @param [elements] - Iterable of elements (or raw values if toElementFn is set). * @param [options] - Options such as comparator and toElementFn. * @returns New Heap instance. */ constructor(elements?: Iterable<E | R>, options?: HeapOptions<E, R>); protected _elements: E[]; /** * Get the backing array of the heap. * @remarks Time O(1), Space O(1) * @returns Internal elements array. */ get elements(): E[]; /** * Get the number of elements. * @remarks Time O(1), Space O(1) * @returns Heap size. */ get size(): number; /** * Get the last leaf element. * @remarks Time O(1), Space O(1) * @returns Last element or undefined. */ get leaf(): E | undefined; /** * Create a heap of the same class from an iterable. * @remarks Time O(N), Space O(N) * @template T * @template R * @template S * @param [elements] - Iterable of elements or raw records. * @param [options] - Heap options including comparator. * @returns A new heap instance of this class. */ static from<T, R = never, S extends Heap<T, R> = Heap<T, R>>(this: new (elements?: Iterable<T | R>, options?: HeapOptions<T, R>) => S, elements?: Iterable<T | R>, options?: HeapOptions<T, R>): S; /** * Build a Heap from an iterable in linear time given a comparator. * @remarks Time O(N), Space O(N) * @template EE * @template RR * @param elements - Iterable of elements. * @param options - Heap options including comparator. * @returns A new Heap built from elements. */ static heapify<EE = unknown, RR = never>(elements: Iterable<EE>, options: HeapOptions<EE, RR>): Heap<EE, RR>; /** * Insert an element. * @remarks Time O(1) amortized, Space O(1) * @param element - Element to insert. * @returns True. */ add(element: E): boolean; /** * Insert many elements from an iterable. * @remarks Time O(N log N), Space O(1) * @param elements - Iterable of elements or raw values. * @returns Array of per-element success flags. */ addMany(elements: Iterable<E | R>): boolean[]; /** * Remove and return the top element. * @remarks Time O(log N), Space O(1) * @returns Top element or undefined. */ poll(): E | undefined; /** * Get the current top element without removing it. * @remarks Time O(1), Space O(1) * @returns Top element or undefined. */ peek(): E | undefined; /** * Check whether the heap is empty. * @remarks Time O(1), Space O(1) * @returns True if size is 0. */ isEmpty(): boolean; /** * Remove all elements. * @remarks Time O(1), Space O(1) * @returns void */ clear(): void; /** * Replace the backing array and rebuild the heap. * @remarks Time O(N), Space O(N) * @param elements - Iterable used to refill the heap. * @returns Array of per-node results from fixing steps. */ refill(elements: Iterable<E>): boolean[]; /** * Check if an equal element exists in the heap. * @remarks Time O(N), Space O(1) * @param element - Element to search for. * @returns True if found. */ has(element: E): boolean; /** * Delete one occurrence of an element. * @remarks Time O(N), Space O(1) * @param element - Element to delete. * @returns True if an element was removed. */ delete(element: E): boolean; /** * Delete the first element that matches a predicate. * @remarks Time O(N), Space O(1) * @param predicate - Function (element, index, heap) → boolean. * @returns True if an element was removed. */ deleteBy(predicate: (element: E, index: number, heap: this) => boolean): boolean; /** * Set the equality comparator used by has/delete operations. * @remarks Time O(1), Space O(1) * @param equals - Equality predicate (a, b) → boolean. * @returns This heap. */ setEquality(equals: (a: E, b: E) => boolean): this; /** * Traverse the binary heap as a complete binary tree and collect elements. * @remarks Time O(N), Space O(H) * @param [order] - Traversal order: 'PRE' | 'IN' | 'POST'. * @returns Array of visited elements. */ dfs(order?: DFSOrderPattern): E[]; /** * Restore heap order bottom-up (heapify in-place). * @remarks Time O(N), Space O(1) * @returns Array of per-node results from fixing steps. */ fix(): boolean[]; /** * Return all elements in ascending order by repeatedly polling. * @remarks Time O(N log N), Space O(N) * @returns Sorted array of elements. */ sort(): E[]; /** * Deep clone this heap. * @remarks Time O(N), Space O(N) * @returns A new heap with the same elements. */ clone(): this; /** * Filter elements into a new heap of the same class. * @remarks Time O(N log N), Space O(N) * @param callback - Predicate (element, index, heap) → boolean to keep element. * @param [thisArg] - Value for `this` inside the callback. * @returns A new heap with the kept elements. */ filter(callback: ElementCallback<E, R, boolean>, thisArg?: unknown): this; /** * Map elements into a new heap of possibly different element type. * @remarks Time O(N log N), Space O(N) * @template EM * @template RM * @param callback - Mapping function (element, index, heap) → newElement. * @param options - Options for the output heap, including comparator for EM. * @param [thisArg] - Value for `this` inside the callback. * @returns A new heap with mapped elements. */ map<EM, RM>(callback: ElementCallback<E, R, EM>, options: HeapOptions<EM, RM> & { comparator: Comparator<EM>; }, thisArg?: unknown): Heap<EM, RM>; /** * Map elements into a new heap of the same element type. * @remarks Time O(N log N), Space O(N) * @param callback - Mapping function (element, index, heap) → element. * @param [thisArg] - Value for `this` inside the callback. * @returns A new heap with mapped elements. */ mapSame(callback: ElementCallback<E, R, E>, thisArg?: unknown): this; protected _DEFAULT_COMPARATOR: (a: E, b: E) => number; protected _comparator: Comparator<E>; /** * Get the comparator used to order elements. * @remarks Time O(1), Space O(1) * @returns Comparator function. */ /** * Get the comparator used to order elements. * @remarks Time O(1), Space O(1) * @returns Comparator function. */ get comparator(): Comparator<E>; protected _getIterator(): IterableIterator<E>; protected _bubbleUp(index: number): boolean; protected _sinkDown(index: number, halfLength: number): boolean; /** * (Protected) Create an empty instance of the same concrete class. * @remarks Time O(1), Space O(1) * @param [options] - Options to override comparator or toElementFn. * @returns A like-kind empty heap instance. */ protected _createInstance(options?: HeapOptions<E, R>): this; /** * (Protected) Create a like-kind instance seeded by elements. * @remarks Time O(N log N), Space O(N) * @template EM * @template RM * @param [elements] - Iterable of elements or raw values to seed. * @param [options] - Options forwarded to the constructor. * @returns A like-kind heap instance. */ protected _createLike<EM, RM>(elements?: Iterable<EM> | Iterable<RM>, options?: HeapOptions<EM, RM>): Heap<EM, RM>; /** * (Protected) Spawn an empty like-kind heap instance. * @remarks Time O(1), Space O(1) * @template EM * @template RM * @param [options] - Options forwarded to the constructor. * @returns An empty like-kind heap instance. */ protected _spawnLike<EM, RM>(options?: HeapOptions<EM, RM>): Heap<EM, RM>; } /** * Node container used by FibonacciHeap. * @remarks Time O(1), Space O(1) * @template E */ export declare class FibonacciHeapNode<E> { element: E; degree: number; left?: FibonacciHeapNode<E>; right?: FibonacciHeapNode<E>; child?: FibonacciHeapNode<E>; parent?: FibonacciHeapNode<E>; marked: boolean; constructor(element: E, degree?: number); } /** * Fibonacci heap (min-heap) optimized for fast merges and amortized operations. * @remarks Time O(1), Space O(1) * @template E * @example examples will be generated by unit test */ export declare class FibonacciHeap<E> { /** * Create a FibonacciHeap. * @remarks Time O(1), Space O(1) * @param [comparator] - Comparator to order elements (min-heap by default). * @returns New FibonacciHeap instance. */ constructor(comparator?: Comparator<E>); protected _root?: FibonacciHeapNode<E>; /** * Get the circular root list head. * @remarks Time O(1), Space O(1) * @returns Root node or undefined. */ get root(): FibonacciHeapNode<E> | undefined; protected _size: number; get size(): number; protected _min?: FibonacciHeapNode<E>; /** * Get the current minimum node. * @remarks Time O(1), Space O(1) * @returns Min node or undefined. */ get min(): FibonacciHeapNode<E> | undefined; protected _comparator: Comparator<E>; get comparator(): Comparator<E>; clear(): void; add(element: E): boolean; /** * Push an element into the root list. * @remarks Time O(1) amortized, Space O(1) * @param element - Element to insert. * @returns This heap. */ push(element: E): this; peek(): E | undefined; /** * Collect nodes from a circular doubly linked list starting at head. * @remarks Time O(K), Space O(K) * @param [head] - Start node of the circular list. * @returns Array of nodes from the list. */ consumeLinkedList(head?: FibonacciHeapNode<E>): FibonacciHeapNode<E>[]; /** * Insert a node into a parent's child list (circular). * @remarks Time O(1), Space O(1) * @param parent - Parent node. * @param node - Child node to insert. * @returns void */ mergeWithChild(parent: FibonacciHeapNode<E>, node: FibonacciHeapNode<E>): void; poll(): E | undefined; /** * Remove and return the minimum element, consolidating the root list. * @remarks Time O(log N) amortized, Space O(1) * @returns Minimum element or undefined. */ pop(): E | undefined; /** * Meld another heap into this heap. * @remarks Time O(1), Space O(1) * @param heapToMerge - Another FibonacciHeap to meld into this one. * @returns void */ merge(heapToMerge: FibonacciHeap<E>): void; _createNode(element: E): FibonacciHeapNode<E>; isEmpty(): boolean; protected _defaultComparator(a: E, b: E): number; protected mergeWithRoot(node: FibonacciHeapNode<E>): void; protected removeFromRoot(node: FibonacciHeapNode<E>): void; protected _link(y: FibonacciHeapNode<E>, x: FibonacciHeapNode<E>): void; protected _consolidate(): void; }