max-priority-queue-typed
Version:
Max Priority Queue
972 lines (871 loc) • 28.8 kB
text/typescript
/**
* 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 class Heap<E = unknown, R = never> extends IterableElementBase<E, R> {
protected _equals: (a: E, b: E) => boolean = Object.is;
/**
* 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>) {
super(options);
if (options) {
const { comparator } = options;
if (comparator) this._comparator = comparator;
}
this.addMany(elements as Iterable<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[] {
return this._elements;
}
/**
* Get the number of elements.
* @remarks Time O(1), Space O(1)
* @returns Heap size.
*/
get size(): number {
return this.elements.length;
}
/**
* Get the last leaf element.
* @remarks Time O(1), Space O(1)
* @returns Last element or undefined.
*/
get leaf(): E | undefined {
return this.elements[this.size - 1] ?? 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 {
return new this(elements, options);
}
/**
* 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> {
return new Heap<EE, RR>(elements, options);
}
/**
* Insert an element.
* @remarks Time O(1) amortized, Space O(1)
* @param element - Element to insert.
* @returns True.
*/
add(element: E): boolean {
this._elements.push(element);
return this._bubbleUp(this.elements.length - 1);
}
/**
* 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[] {
const flags: boolean[] = [];
for (const el of elements) {
if (this.toElementFn) {
const ok = this.add(this.toElementFn(el as R));
flags.push(ok);
} else {
const ok = this.add(el as E);
flags.push(ok);
}
}
return flags;
}
/**
* Remove and return the top element.
* @remarks Time O(log N), Space O(1)
* @returns Top element or undefined.
*/
poll(): E | undefined {
if (this.elements.length === 0) return;
const value = this.elements[0];
const last = this.elements.pop()!;
if (this.elements.length) {
this.elements[0] = last;
this._sinkDown(0, this.elements.length >> 1);
}
return value;
}
/**
* Get the current top element without removing it.
* @remarks Time O(1), Space O(1)
* @returns Top element or undefined.
*/
peek(): E | undefined {
return this.elements[0];
}
/**
* Check whether the heap is empty.
* @remarks Time O(1), Space O(1)
* @returns True if size is 0.
*/
isEmpty(): boolean {
return this.size === 0;
}
/**
* Remove all elements.
* @remarks Time O(1), Space O(1)
* @returns void
*/
clear(): void {
this._elements = [];
}
/**
* 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[] {
this._elements = Array.from(elements);
return this.fix();
}
/**
* 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.
*/
override has(element: E): boolean {
for (const el of this.elements) if (this._equals(el, element)) return true;
return false;
}
/**
* 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 {
let index = -1;
for (let i = 0; i < this.elements.length; i++) {
if (this._equals(this.elements[i], element)) {
index = i;
break;
}
}
if (index < 0) return false;
if (index === 0) {
this.poll();
} else if (index === this.elements.length - 1) {
this.elements.pop();
} else {
this.elements.splice(index, 1, this.elements.pop()!);
this._bubbleUp(index);
this._sinkDown(index, this.elements.length >> 1);
}
return true;
}
/**
* 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 {
let idx = -1;
for (let i = 0; i < this.elements.length; i++) {
if (predicate(this.elements[i], i, this)) {
idx = i;
break;
}
}
if (idx < 0) return false;
if (idx === 0) {
this.poll();
} else if (idx === this.elements.length - 1) {
this.elements.pop();
} else {
this.elements.splice(idx, 1, this.elements.pop()!);
this._bubbleUp(idx);
this._sinkDown(idx, this.elements.length >> 1);
}
return true;
}
/**
* 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 {
this._equals = equals;
return 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 = 'PRE'): E[] {
const result: E[] = [];
const _dfs = (index: number) => {
const left = 2 * index + 1,
right = left + 1;
if (index < this.size) {
if (order === 'IN') {
_dfs(left);
result.push(this.elements[index]);
_dfs(right);
} else if (order === 'PRE') {
result.push(this.elements[index]);
_dfs(left);
_dfs(right);
} else if (order === 'POST') {
_dfs(left);
_dfs(right);
result.push(this.elements[index]);
}
}
};
_dfs(0);
return result;
}
/**
* 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[] {
const results: boolean[] = [];
for (let i = Math.floor(this.size / 2) - 1; i >= 0; i--) {
results.push(this._sinkDown(i, this.elements.length >> 1));
}
return results;
}
/**
* 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[] {
const visited: E[] = [];
const cloned = this._createInstance();
for (const x of this.elements) cloned.add(x);
while (!cloned.isEmpty()) {
const top = cloned.poll();
if (top !== undefined) visited.push(top);
}
return visited;
}
/**
* Deep clone this heap.
* @remarks Time O(N), Space O(N)
* @returns A new heap with the same elements.
*/
clone(): this {
const next = this._createInstance();
for (const x of this.elements) next.add(x);
return next;
}
/**
* 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 {
const out = this._createInstance();
let i = 0;
for (const x of this) {
if (thisArg === undefined ? callback(x, i++, this) : callback.call(thisArg, x, i++, this)) {
out.add(x);
} else {
i++;
}
}
return out;
}
/**
* 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> {
const { comparator, toElementFn, ...rest } = options ?? {};
if (!comparator) throw new TypeError('Heap.map requires options.comparator for EM');
const out = this._createLike<EM, RM>([], { ...rest, comparator, toElementFn });
let i = 0;
for (const x of this) {
const v = thisArg === undefined ? callback(x, i++, this) : callback.call(thisArg, x, i++, this);
out.add(v);
}
return out;
}
/**
* 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 {
const out = this._createInstance();
let i = 0;
for (const x of this) {
const v = thisArg === undefined ? callback(x, i++, this) : callback.call(thisArg, x, i++, this);
out.add(v);
}
return out;
}
protected _DEFAULT_COMPARATOR = (a: E, b: E): number => {
if (typeof a === 'object' || typeof b === 'object') {
throw TypeError('When comparing object types, define a custom comparator in options.');
}
if ((a as unknown as number) > (b as unknown as number)) return 1;
if ((a as unknown as number) < (b as unknown as number)) return -1;
return 0;
};
protected _comparator: Comparator<E> = this._DEFAULT_COMPARATOR; /**
* 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() {
return this._comparator;
}
protected *_getIterator(): IterableIterator<E> {
for (const element of this.elements) yield element;
}
protected _bubbleUp(index: number): boolean {
const element = this.elements[index];
while (index > 0) {
const parent = (index - 1) >> 1;
const parentItem = this.elements[parent];
if (this.comparator(parentItem, element) <= 0) break;
this.elements[index] = parentItem;
index = parent;
}
this.elements[index] = element;
return true;
}
protected _sinkDown(index: number, halfLength: number): boolean {
const element = this.elements[index];
while (index < halfLength) {
let left = (index << 1) | 1;
const right = left + 1;
let minItem = this.elements[left];
if (right < this.elements.length && this.comparator(minItem, this.elements[right]) > 0) {
left = right;
minItem = this.elements[right];
}
if (this.comparator(minItem, element) >= 0) break;
this.elements[index] = minItem;
index = left;
}
this.elements[index] = element;
return true;
}
/**
* (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 {
const Ctor: any = this.constructor;
const next: any = new Ctor([], { comparator: this.comparator, toElementFn: this.toElementFn, ...(options ?? {}) });
return next as 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> {
const Ctor: any = this.constructor;
return new Ctor(elements, options) as 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> {
return this._createLike<EM, RM>([], options);
}
}
/**
* Node container used by FibonacciHeap.
* @remarks Time O(1), Space O(1)
* @template E
*/
export 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 = 0) {
this.element = element;
this.degree = degree;
this.marked = false;
}
}
/**
* 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 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>) {
this.clear();
this._comparator = comparator || this._defaultComparator;
if (typeof this.comparator !== 'function') throw new Error('FibonacciHeap: comparator must be a function.');
}
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 {
return this._root;
}
protected _size = 0;
get size(): number {
return this._size;
}
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 {
return this._min;
}
protected _comparator: Comparator<E>;
get comparator(): Comparator<E> {
return this._comparator;
}
clear(): void {
this._root = undefined;
this._min = undefined;
this._size = 0;
}
add(element: E): boolean {
this.push(element);
return true;
}
/**
* 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 {
const node = this._createNode(element);
node.left = node;
node.right = node;
this.mergeWithRoot(node);
if (!this.min || this.comparator(node.element, this.min.element) <= 0) this._min = node;
this._size++;
return this;
}
peek(): E | undefined {
return this.min ? this.min.element : 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>[] {
const elements: FibonacciHeapNode<E>[] = [];
if (!head) return elements;
let node: FibonacciHeapNode<E> | undefined = head;
let started = false;
while (true) {
if (node === head && started) break;
else if (node === head) started = true;
elements.push(node!);
node = node!.right;
}
return elements;
}
/**
* 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 {
if (!parent.child) parent.child = node;
else {
node.right = parent.child.right;
node.left = parent.child;
parent.child.right!.left = node;
parent.child.right = node;
}
}
poll(): E | undefined {
return this.pop();
}
/**
* 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 {
if (this._size === 0) return undefined;
const z = this.min!;
if (z.child) {
const elements = this.consumeLinkedList(z.child);
for (const node of elements) {
this.mergeWithRoot(node);
node.parent = undefined;
}
}
this.removeFromRoot(z);
if (z === z.right) {
this._min = undefined;
this._root = undefined;
} else {
this._min = z.right;
this._consolidate();
}
this._size--;
return z.element;
}
/**
* 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 {
if (heapToMerge.size === 0) return;
if (this.root && heapToMerge.root) {
const thisRoot = this.root,
otherRoot = heapToMerge.root;
const thisRootRight = thisRoot.right!,
otherRootLeft = otherRoot.left!;
thisRoot.right = otherRoot;
otherRoot.left = thisRoot;
thisRootRight.left = otherRootLeft;
otherRootLeft.right = thisRootRight;
} else if (!this.root && heapToMerge.root) {
this._root = heapToMerge.root;
}
if (!this.min || (heapToMerge.min && this.comparator(heapToMerge.min.element, this.min.element) < 0)) {
this._min = heapToMerge.min;
}
this._size += heapToMerge.size;
heapToMerge.clear();
}
_createNode(element: E): FibonacciHeapNode<E> {
return new FibonacciHeapNode<E>(element);
}
isEmpty(): boolean {
return this._size === 0;
}
protected _defaultComparator(a: E, b: E): number {
if (a < b) return -1;
if (a > b) return 1;
return 0;
}
protected mergeWithRoot(node: FibonacciHeapNode<E>): void {
if (!this.root) this._root = node;
else {
node.right = this.root.right;
node.left = this.root;
this.root.right!.left = node;
this.root.right = node;
}
}
protected removeFromRoot(node: FibonacciHeapNode<E>): void {
if (this.root === node) this._root = node.right;
if (node.left) node.left.right = node.right;
if (node.right) node.right.left = node.left;
}
protected _link(y: FibonacciHeapNode<E>, x: FibonacciHeapNode<E>): void {
this.removeFromRoot(y);
y.left = y;
y.right = y;
this.mergeWithChild(x, y);
x.degree++;
y.parent = x;
}
protected _consolidate(): void {
const A: (FibonacciHeapNode<E> | undefined)[] = new Array(this._size);
const elements = this.consumeLinkedList(this.root);
let x: FibonacciHeapNode<E> | undefined,
y: FibonacciHeapNode<E> | undefined,
d: number,
t: FibonacciHeapNode<E> | undefined;
for (const node of elements) {
x = node;
d = x.degree;
while (A[d]) {
y = A[d] as FibonacciHeapNode<E>;
if (this.comparator(x.element, y.element) > 0) {
t = x;
x = y;
y = t;
}
this._link(y, x);
A[d] = undefined;
d++;
}
A[d] = x;
}
for (let i = 0; i < A.length; i++) {
if (A[i] && (!this.min || this.comparator(A[i]!.element, this.min.element) <= 0)) this._min = A[i]!;
}
}
}