src/FibonacciHeap.js
import {
_append as list_insert,
_remove as list_remove,
_concat as list_concatenate,
} from '@data-structure-algebra/circularly-linked-list';
import Node from './Node.js';
import list_reset_parent from './list_reset_parent.js';
import consolidate from './consolidate.js';
import cut from './cut.js';
import cascading_cut from './cascading_cut.js';
/**
* See CLRS09 Chapter 19 on the Fibonacci Heap.
*/
export default class FibonacciHeap {
/**
* Make-heap: creates a new empty heap.
*
* To make an empty Fibonacci heap, the MAKE-FIB-HEAP procedure
* allocates and returns the Fibonacci heap object H, where H.n = 0 and
* H.min = NIL ; there are no trees in H. Because t(H) = 0 and m(H) = 0,
* the potential of the empty Fibonacci heap is pot(H) = 0. The amortized
* cost of MAKE-FIB-HEAP is thus equal to its O(1) actual cost.
*/
constructor(compare) {
this.compare = compare; // Comparison function
/**
* We access a given Fibonacci heap H by a pointer H.min to the root of
* a tree containing the minimum key; we call this node the minimum
* node of the Fibonacci heap. If more than one root has a key with the
* minimum value, then any such root may serve as the minimum node.
* When a Fibonacci heap H is empty, H.min is NIL.
*/
this.min = null;
}
/**
* Find-min: simply return the top element of the heap.
*/
head() {
if (this.min === null) return undefined;
return this.min.value;
}
/**
* Minimum: return a pointer to the element whose key is minimum
*
* The minimum node of a Fibonacci heap H is given by the pointer H.min, so
* we can find the minimum node in O(1) actual time. Because the potential
* of H does not change, the amortized cost of this operation is equal to
* its O(1) actual cost.
*/
headreference() {
return this.min;
}
/**
*/
pop() {
const min = this.popreference();
return min === null ? undefined : min.value;
}
/**
* FIB-HEAP-EXTRACT-MIN(H):
*
* Extracting the minimum node
*
* The process of extracting the minimum node is the most complicated of
* the operations presented in this section. It is also where the delayed
* work of consolidating trees in the root list finally occurs. The
* following pseudocode extracts the minimum node. The code assumes for
* convenience that when a node is removed from a linked list, pointers
* remaining in the list are updated, but pointers in the extracted node
* are left unchanged. It also calls the auxiliary procedure CONSOLIDATE,
* which we shall see shortly.
*/
popreference() {
const z = this.min;
if (z === null) return null;
if (z.children !== null) {
list_reset_parent(z.children);
list_concatenate(z, z.children);
}
if (z === z.next) this.min = null;
else {
list_remove(z);
this.min = consolidate(this.compare, z.next);
}
z.next = z; // (Re)move this?
z.prev = z;
z.children = null;
z.degree = 0;
z.mark = false;
return z;
}
/**
* Create a new node for the inserted element and put it into the heap.
*/
push(value) {
const node = new Node(value);
return this.pushreference(node);
}
/**
* Insert: insert new node.
* /!\ ref.next = ref.prev = ref which means all references that are
* external to the tree must reset .next and .prev and one must not call
* FibonacciHeap#pushreference with an internal reference from this tree or
* another, except the root of another tree.
*
* Change in potential is 1. Therefore amortized cost is O(1).
*/
pushreference(ref) {
if (this.min === null) {
// Create a root list for H containing just x (by precondition)
this.min = ref;
} else {
// This.min != null != ref
// Insert x into H's root list
list_insert(this.min, ref);
// Update minimum
if (this.compare(ref.value, this.min.value) < 0) {
this.min = ref;
}
}
return ref;
}
/**
* Union: Uniting two Fibonacci heaps
*
* The following procedure unites Fibonacci heaps H_1 and H_2, destroying
* H_1 and H_2 in the process. It simply concatenates the root lists of H_1
* and H_2 and then determines the new minimum node. Afterward, the objects
* representing H_1 and H_2 will never be used again.
*
* /!\ Assumes the same comparison function is used in both trees.
*
* Change in potential is zero. Amortized cost is O(1), the actual cost.
*/
meld(other) {
const ref = other.min;
if (ref === null) return;
if (this.min === null) this.min = ref;
else {
// This.min != null != ref
// Concatenate the root list of H_2 with the root list of H
list_concatenate(this.min, ref);
// Update minimum
if (this.compare(ref.value, this.min.value) < 0) {
this.min = ref;
}
}
}
/**
* Synonym for FibonacciHeap#meld.
* TODO Remove this.
*/
merge(other) {
this.meld(other);
}
/**
* @param {Node} ref Non-null internal node object.
* @param {Object} value The new value for ref.
*/
update(ref, value) {
const d = this.compare(value, ref.value);
if (d < 0) this.decreasekey(ref, value);
else if (d > 0) this.increasekey(ref, value);
else ref.value = value;
}
/**
* FIB-HEAP-DECREASE-kEY:
* @param {Node} ref Non-null internal node object.
* @param {Object} value The new value for ref.
*/
decreasekey(ref, value) {
ref.value = value;
if (ref !== this.min) {
// This.min != null, ref != null
const y = ref.parent;
if (y !== null && this.compare(ref.value, y.value) < 0) {
cut(ref, y);
list_insert(this.min, ref);
for (const x of cascading_cut(y)) list_insert(this.min, x);
}
if (this.compare(ref.value, this.min.value) < 0) {
this.min = ref;
}
}
}
/**
* Increase-key: remove the item at the key to be increased, replace
* the key with a larger key, then push the result back into the heap.
*
* @param {Node} ref Non-null internal node object.
* @param {Object} value The new value for ref.
*
*/
increasekey(ref, value) {
this.delete(ref);
ref.value = value;
this.pushreference(ref);
}
/**
* Delete
* ref must be internal
* ref.prev and ref.next get reset to null
*/
delete(ref) {
if (ref !== this.min) {
// This.min != null, ref != null
const y = ref.parent;
if (y !== null) {
cut(ref, y);
list_insert(this.min, ref);
for (const x of cascading_cut(y)) list_insert(this.min, x);
}
this.min = ref;
}
this.popreference();
}
}