123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407 |
- /**
- * Copyright (c) 2022 mol* contributors, licensed under MIT, See LICENSE file for more info.
- *
- * @author Gianluca Tomasello <giagitom@gmail.com>
- *
- * Adapted from https://github.com/gwtw/ts-fibonacci-heap, Copyright (c) 2014 Daniel Imms, MIT
- */
- interface INode<K, V> {
- key: K;
- value?: V;
- }
- type CompareFunction<K, V> = (a: INode<K, V>, b: INode<K, V>) => number;
- class Node<K, V> implements INode<K, V> {
- public key: K;
- public value: V | undefined;
- public prev: Node<K, V>;
- public next: Node<K, V>;
- public parent: Node<K, V> | null = null;
- public child: Node<K, V> | null = null;
- public degree: number = 0;
- public isMarked: boolean = false;
- constructor(key: K, value?: V) {
- this.key = key;
- this.value = value;
- this.prev = this;
- this.next = this;
- }
- }
- class NodeListIterator<K, V> {
- private _index: number;
- private _items: Node<K, V>[];
- private _len: number;
- /**
- * Creates an Iterator used to simplify the consolidate() method. It works by
- * making a shallow copy of the nodes in the root list and iterating over the
- * shallow copy instead of the source as the source will be modified.
- * @param start A node from the root list.
- */
- constructor(start?: Node<K, V>) {
- this._index = -1;
- this._items = [];
- this._len = 0;
- if (start) {
- let current = start, l = 0;
- do {
- this._items[l++] = current;
- current = current.next;
- } while (start !== current);
- this._len = l;
- }
- }
- /**
- * @return Whether there is a next node in the iterator.
- */
- public hasNext(): boolean {
- return this._index < this._len - 1;
- }
- /**
- * @return The next node.
- */
- public next(): Node<K, V> {
- return this._items[++this._index];
- }
- /**
- * @return Resets iterator to reuse it.
- */
- public reset(start: Node<K, V>) {
- this._index = -1;
- this._len = 0;
- let current = start, l = 0;
- do {
- this._items[l++] = current;
- current = current.next;
- } while (start !== current);
- this._len = l;
- }
- }
- const tmpIt = new NodeListIterator<any, any>();
- /**
- * A Fibonacci heap data structure with a key and optional value.
- */
- export class FibonacciHeap<K, V> {
- private _minNode: Node<K, V> | null = null;
- private _nodeCount: number = 0;
- private _compare: CompareFunction<K, V>;
- constructor(
- compare?: CompareFunction<K, V>
- ) {
- this._compare = compare ? compare : this._defaultCompare;
- }
- /**
- * Clears the heap's data, making it an empty heap.
- */
- public clear(): void {
- this._minNode = null;
- this._nodeCount = 0;
- }
- /**
- * Decreases a key of a node.
- * @param node The node to decrease the key of.
- * @param newKey The new key to assign to the node.
- */
- public decreaseKey(node: Node<K, V>, newKey: K): void {
- if (!node) {
- throw new Error('Cannot decrease key of non-existent node');
- }
- if (this._compare({ key: newKey }, { key: node.key }) > 0) {
- throw new Error('New key is larger than old key');
- }
- node.key = newKey;
- const parent = node.parent;
- if (parent && this._compare(node, parent) < 0) {
- this._cut(node, parent, <Node<K, V>> this._minNode);
- this._cascadingCut(parent, <Node<K, V>> this._minNode);
- }
- if (this._compare(node, <Node<K, V>> this._minNode) < 0) {
- this._minNode = node;
- }
- }
- /**
- * Deletes a node.
- * @param node The node to delete.
- */
- public delete(node: Node<K, V>): void {
- // This is a special implementation of decreaseKey that sets the argument to
- // the minimum value. This is necessary to make generic keys work, since there
- // is no MIN_VALUE constant for generic types.
- const parent = node.parent;
- if (parent) {
- this._cut(node, parent, <Node<K, V>> this._minNode);
- this._cascadingCut(parent, <Node<K, V>> this._minNode);
- }
- this._minNode = node;
- this.extractMinimum();
- }
- /**
- * Extracts and returns the minimum node from the heap.
- * @return The heap's minimum node or null if the heap is empty.
- */
- public extractMinimum(): Node<K, V> | null {
- const extractedMin = this._minNode;
- if (extractedMin) {
- // Set parent to null for the minimum's children
- if (extractedMin.child) {
- let child = extractedMin.child;
- do {
- child.parent = null;
- child = child.next;
- } while (child !== extractedMin.child);
- }
- let nextInRootList = null;
- if (extractedMin.next !== extractedMin) {
- nextInRootList = extractedMin.next;
- }
- // Remove min from root list
- this._removeNodeFromList(extractedMin);
- this._nodeCount--;
- // Merge the children of the minimum node with the root list
- this._minNode = this._mergeLists(nextInRootList, extractedMin.child);
- if (this._minNode) {
- this._minNode = this._consolidate(this._minNode);
- }
- }
- return extractedMin;
- }
- /**
- * Returns the minimum node from the heap.
- * @return The heap's minimum node or null if the heap is empty.
- */
- public findMinimum(): Node<K, V> | null {
- return this._minNode;
- }
- /**
- * Inserts a new key-value pair into the heap.
- * @param key The key to insert.
- * @param value The value to insert.
- * @return node The inserted node.
- */
- public insert(key: K, value?: V): Node<K, V> {
- const node = new Node(key, value);
- this._minNode = this._mergeLists(this._minNode, node);
- this._nodeCount++;
- return node;
- }
- /**
- * @return Whether the heap is empty.
- */
- public isEmpty(): boolean {
- return this._minNode === null;
- }
- /**
- * @return The size of the heap.
- */
- public size(): number {
- if (this._minNode === null) {
- return 0;
- }
- return this._getNodeListSize(this._minNode);
- }
- /**
- * Joins another heap to this heap.
- * @param other The other heap.
- */
- public union(other: FibonacciHeap<K, V>): void {
- this._minNode = this._mergeLists(this._minNode, other._minNode);
- this._nodeCount += other._nodeCount;
- }
- /**
- * Compares two nodes with each other.
- * @param a The first key to compare.
- * @param b The second key to compare.
- * @return -1, 0 or 1 if a < b, a == b or a > b respectively.
- */
- private _defaultCompare(a: INode<K, V>, b: INode<K, V>): number {
- if (a.key > b.key) {
- return 1;
- }
- if (a.key < b.key) {
- return -1;
- }
- return 0;
- }
- /**
- * Cut the link between a node and its parent, moving the node to the root list.
- * @param node The node being cut.
- * @param parent The parent of the node being cut.
- * @param minNode The minimum node in the root list.
- * @return The heap's new minimum node.
- */
- private _cut(node: Node<K, V>, parent: Node<K, V>, minNode: Node<K, V>): Node<K, V> | null {
- node.parent = null;
- parent.degree--;
- if (node.next === node) {
- parent.child = null;
- } else {
- parent.child = node.next;
- }
- this._removeNodeFromList(node);
- const newMinNode = this._mergeLists(minNode, node);
- node.isMarked = false;
- return newMinNode;
- }
- /**
- * Perform a cascading cut on a node; mark the node if it is not marked,
- * otherwise cut the node and perform a cascading cut on its parent.
- * @param node The node being considered to be cut.
- * @param minNode The minimum node in the root list.
- * @return The heap's new minimum node.
- */
- private _cascadingCut(node: Node<K, V>, minNode: Node<K, V> | null): Node<K, V> | null {
- const parent = node.parent;
- if (parent) {
- if (node.isMarked) {
- minNode = this._cut(node, parent, <Node<K, V>>minNode);
- minNode = this._cascadingCut(parent, minNode);
- } else {
- node.isMarked = true;
- }
- }
- return minNode;
- }
- /**
- * Merge all trees of the same order together until there are no two trees of
- * the same order.
- * @param minNode The current minimum node.
- * @return The new minimum node.
- */
- private _consolidate(minNode: Node<K, V>): Node<K, V> | null {
- const aux = [];
- tmpIt.reset(minNode);
- while (tmpIt.hasNext()) {
- let current = tmpIt.next();
- // If there exists another node with the same degree, merge them
- let auxCurrent = aux[current.degree];
- while (auxCurrent) {
- if (this._compare(current, auxCurrent) > 0) {
- const temp = current;
- current = auxCurrent;
- auxCurrent = temp;
- }
- this._linkHeaps(auxCurrent, current);
- aux[current.degree] = null;
- current.degree++;
- auxCurrent = aux[current.degree];
- }
- aux[current.degree] = current;
- }
- let newMinNode = null;
- for (let i = 0; i < aux.length; i++) {
- const node = aux[i];
- if (node) {
- // Remove siblings before merging
- node.next = node;
- node.prev = node;
- newMinNode = this._mergeLists(newMinNode, node);
- }
- }
- return newMinNode;
- }
- /**
- * Removes a node from a node list.
- * @param node The node to remove.
- */
- private _removeNodeFromList(node: Node<K, V>): void {
- const prev = node.prev;
- const next = node.next;
- prev.next = next;
- next.prev = prev;
- node.next = node;
- node.prev = node;
- }
- /**
- * Links two heaps of the same order together.
- *
- * @private
- * @param max The heap with the larger root.
- * @param min The heap with the smaller root.
- */
- private _linkHeaps(max: Node<K, V>, min: Node<K, V>): void {
- this._removeNodeFromList(max);
- min.child = this._mergeLists(max, min.child);
- max.parent = min;
- max.isMarked = false;
- }
- /**
- * Merge two lists of nodes together.
- *
- * @private
- * @param a The first list to merge.
- * @param b The second list to merge.
- * @return The new minimum node from the two lists.
- */
- private _mergeLists(a: Node<K, V> | null, b: Node<K, V> | null): Node<K, V> | null {
- if (!a) {
- if (!b) {
- return null;
- }
- return b;
- }
- if (!b) {
- return a;
- }
- const temp = a.next;
- a.next = b.next;
- a.next.prev = a;
- b.next = temp;
- b.next.prev = b;
- return this._compare(a, b) < 0 ? a : b;
- }
- /**
- * Gets the size of a node list.
- * @param node A node within the node list.
- * @return The size of the node list.
- */
- private _getNodeListSize(node: Node<K, V>): number {
- let count = 0;
- let current = node;
- do {
- count++;
- if (current.child) {
- count += this._getNodeListSize(current.child);
- }
- current = current.next;
- } while (current !== node);
- return count;
- }
- }
|