/** * data-structure-typed * * @author Pablo Zeng * @copyright Copyright (c) 2022 Pablo Zeng * @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 Typical operations: O(log N) insert/remove, O(1) peek. Space O(N). * @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 solve top k problems * function topKElements(arr: number[], k: number): number[] { * const heap = new Heap([], { 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 dynamically maintain the median * class MedianFinder { * private low: MaxHeap; // Max heap, stores the smaller half * private high: MinHeap; // Min heap, stores the larger half * * constructor() { * this.low = new MaxHeap([]); * this.high = new MinHeap([]); * } * * 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 { * const machineHeap = new Heap<{ id: number; load: number }>([], { comparator: (a, b) => a.load - b.load }); // Min heap * const allocation = new Map(); * * // 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(); * expectedMap.set(0, [ * ['Task1', 3], * ['Task4', 5] * ]); * expectedMap.set(1, [ * ['Task2', 1], * ['Task3', 2], * ['Task5', 4] * ]); * console.log(scheduleTasks(tasks, 2)); // expectedMap; * @example * // Get all elements as array * const heap = new Heap([5, 1, 3, 2, 4]); * const arr = heap.toArray(); * console.log(arr.length); // 5; * console.log(arr.sort()); // [1, 2, 3, 4, 5]; */ export declare class Heap extends IterableElementBase { 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, options?: HeapOptions); 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. * @example * // Track heap capacity * const heap = new Heap(); * console.log(heap.size); // 0; * heap.add(10); * heap.add(20); * console.log(heap.size); // 2; * heap.poll(); * console.log(heap.size); // 1; */ 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 = Heap>(this: new (elements?: Iterable, options?: HeapOptions) => S, elements?: Iterable, options?: HeapOptions): 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(elements: Iterable, options: HeapOptions): Heap; /** * Insert an element. * @remarks Time O(log N) amortized, Space O(1) * @param element - Element to insert. * @returns True. * @example * // basic Heap creation and add operation * // Create a min heap (default) * const minHeap = new Heap([5, 3, 7, 1, 9, 2]); * * // Verify size * console.log(minHeap.size); // 6; * * // Add new element * minHeap.add(4); * console.log(minHeap.size); // 7; * * // Min heap property: smallest element at root * const min = minHeap.peek(); * console.log(min); // 1; */ 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. * @example * // Add multiple elements * const heap = new Heap([], { comparator: (a, b) => a - b }); * heap.addMany([5, 3, 7, 1]); * console.log(heap.peek()); // 1; * console.log(heap.size); // 4; */ addMany(elements: Iterable): boolean[]; /** * Remove and return the top element. * @remarks Time O(log N), Space O(1) * @returns Top element or undefined. * @example * // Heap with custom comparator (MaxHeap behavior) * interface Task { * id: number; * priority: number; * name: string; * } * * // Custom comparator for max heap behavior (higher priority first) * const tasks: Task[] = [ * { id: 1, priority: 5, name: 'Email' }, * { id: 2, priority: 3, name: 'Chat' }, * { id: 3, priority: 8, name: 'Alert' } * ]; * * const maxHeap = new Heap(tasks, { * comparator: (a: Task, b: Task) => b.priority - a.priority * }); * * console.log(maxHeap.size); // 3; * * // Peek returns highest priority task * const topTask = maxHeap.peek(); * console.log(topTask?.priority); // 8; * console.log(topTask?.name); // 'Alert'; */ /** * @deprecated Use `pop` instead. Will be removed in a future major version. * @example * // Heap with custom comparator (MaxHeap behavior) * interface Task { * id: number; * priority: number; * name: string; * } * * // Custom comparator for max heap behavior (higher priority first) * const tasks: Task[] = [ * { id: 1, priority: 5, name: 'Email' }, * { id: 2, priority: 3, name: 'Chat' }, * { id: 3, priority: 8, name: 'Alert' } * ]; * * const maxHeap = new Heap(tasks, { * comparator: (a: Task, b: Task) => b.priority - a.priority * }); * * console.log(maxHeap.size); // 3; * * // Peek returns highest priority task * const topTask = maxHeap.peek(); * console.log(topTask?.priority); // 8; * console.log(topTask?.name); // 'Alert'; */ poll(): E | undefined; /** * Remove and return the top element (min or max depending on comparator). * @remarks Time O(log N) amortized, Space O(1) * @returns The removed top element, or undefined if empty. */ pop(): E | undefined; /** * Get the current top element without removing it. * @remarks Time O(1), Space O(1) * @returns Top element or undefined. * @example * // Heap for event processing with priority * interface Event { * id: number; * type: 'critical' | 'warning' | 'info'; * timestamp: number; * message: string; * } * * // Custom priority: critical > warning > info * const priorityMap = { critical: 3, warning: 2, info: 1 }; * * const eventHeap = new Heap([], { * comparator: (a: Event, b: Event) => { * const priorityA = priorityMap[a.type]; * const priorityB = priorityMap[b.type]; * return priorityB - priorityA; // Higher priority first * } * }); * * // Add events in random order * eventHeap.add({ id: 1, type: 'info', timestamp: 100, message: 'User logged in' }); * eventHeap.add({ id: 2, type: 'critical', timestamp: 101, message: 'Server down' }); * eventHeap.add({ id: 3, type: 'warning', timestamp: 102, message: 'High memory' }); * eventHeap.add({ id: 4, type: 'info', timestamp: 103, message: 'Cache cleared' }); * eventHeap.add({ id: 5, type: 'critical', timestamp: 104, message: 'Database error' }); * * console.log(eventHeap.size); // 5; * * // Process events by priority (critical first) * const processedOrder: Event[] = []; * while (eventHeap.size > 0) { * const event = eventHeap.poll(); * if (event) { * processedOrder.push(event); * } * } * * // Verify critical events came first * console.log(processedOrder[0].type); // 'critical'; * console.log(processedOrder[1].type); // 'critical'; * console.log(processedOrder[2].type); // 'warning'; * console.log(processedOrder[3].type); // 'info'; * console.log(processedOrder[4].type); // 'info'; * * // Verify O(log n) operations * const newHeap = new Heap([5, 3, 7, 1]); * * // Add - O(log n) * newHeap.add(2); * console.log(newHeap.size); // 5; * * // Poll - O(log n) * const removed = newHeap.poll(); * console.log(removed); // 1; * * // Peek - O(1) * const top = newHeap.peek(); * console.log(top); // 2; */ peek(): E | undefined; /** * Check whether the heap is empty. * @remarks Time O(1), Space O(1) * @returns True if size is 0. * @example * // Check if heap is empty * const heap = new Heap([], { comparator: (a, b) => a - b }); * console.log(heap.isEmpty()); // true; * heap.add(1); * console.log(heap.isEmpty()); // false; */ isEmpty(): boolean; /** * Remove all elements. * @remarks Time O(1), Space O(1) * @returns void * @example * // Remove all elements * const heap = new Heap([1, 2, 3], { comparator: (a, b) => a - b }); * heap.clear(); * console.log(heap.isEmpty()); // true; */ clear(): void; /** * 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. * @example * // Check element existence * const heap = new Heap([3, 1, 2], { comparator: (a, b) => a - b }); * console.log(heap.has(1)); // true; * console.log(heap.has(99)); // false; */ 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. * @example * // Remove specific element * const heap = new Heap([3, 1, 4, 1, 5], { comparator: (a, b) => a - b }); * heap.delete(4); * console.log(heap.toArray().includes(4)); // false; */ delete(element: E): boolean; /** * @deprecated Use `deleteWhere` instead. Will be removed in a future major version. */ deleteBy(predicate: (element: E, index: number, heap: this) => boolean): 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. */ deleteWhere(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. * @example * // Depth-first traversal * const heap = new Heap([3, 1, 2], { comparator: (a, b) => a - b }); * const result = heap.dfs('IN'); * console.log(result.length); // 3; */ 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. * @example * // Sort elements using heap * const heap = new Heap([5, 1, 3, 2, 4]); * const sorted = heap.sort(); * console.log(sorted); // [1, 2, 3, 4, 5]; */ sort(): E[]; /** * Deep clone this heap. * @remarks Time O(N), Space O(N) * @returns A new heap with the same elements. * @example * // Create independent copy * const heap = new Heap([3, 1, 4], { comparator: (a, b) => a - b }); * const copy = heap.clone(); * copy.poll(); * console.log(heap.size); // 3; * console.log(copy.size); // 2; */ 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. * @example * // Filter elements * const heap = new Heap([1, 2, 3, 4, 5], { comparator: (a, b) => a - b }); * const evens = heap.filter(x => x % 2 === 0); * console.log(evens.size); // 2; */ filter(callback: ElementCallback, 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. * @example * // Transform elements * const heap = new Heap([1, 2, 3], { comparator: (a, b) => a - b }); * const doubled = heap.map(x => x * 2, { comparator: (a, b) => a - b }); * console.log(doubled.peek()); // 2; */ map(callback: ElementCallback, options: HeapOptions & { comparator: Comparator; }, thisArg?: unknown): Heap; /** * 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, thisArg?: unknown): this; protected readonly _DEFAULT_COMPARATOR: Comparator; protected readonly _comparator: Comparator; /** * Get the comparator used to order elements. * @remarks Time O(1), Space O(1) * @returns Comparator function. */ get comparator(): Comparator; protected _getIterator(): IterableIterator; 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): 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(elements?: Iterable | Iterable, options?: HeapOptions): Heap; /** * (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(options?: HeapOptions): Heap; } /** * Node container used by FibonacciHeap. * @remarks Time O(1), Space O(1) * @template E */ export declare class FibonacciHeapNode { element: E; degree: number; left?: FibonacciHeapNode; right?: FibonacciHeapNode; child?: FibonacciHeapNode; parent?: FibonacciHeapNode; 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 { /** * 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); protected _root?: FibonacciHeapNode; /** * Get the circular root list head. * @remarks Time O(1), Space O(1) * @returns Root node or undefined. */ get root(): FibonacciHeapNode | undefined; protected _size: number; get size(): number; protected _min?: FibonacciHeapNode; /** * Get the current minimum node. * @remarks Time O(1), Space O(1) * @returns Min node or undefined. */ get min(): FibonacciHeapNode | undefined; protected readonly _comparator: Comparator; get comparator(): Comparator; 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 True when the element is added. */ push(element: E): boolean; 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): FibonacciHeapNode[]; /** * 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, node: FibonacciHeapNode): 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): void; createNode(element: E): FibonacciHeapNode; isEmpty(): boolean; protected _defaultComparator(a: E, b: E): number; protected mergeWithRoot(node: FibonacciHeapNode): void; protected removeFromRoot(node: FibonacciHeapNode): void; protected _link(y: FibonacciHeapNode, x: FibonacciHeapNode): void; protected _consolidate(): void; }