You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
321 lines
6.3 KiB
321 lines
6.3 KiB
/* eslint no-constant-condition: 0 */
|
|
/**
|
|
* Mnemonist Fibonacci Heap
|
|
* =========================
|
|
*
|
|
* Fibonacci heap implementation.
|
|
*/
|
|
var comparators = require('./utils/comparators.js'),
|
|
forEach = require('obliterator/foreach');
|
|
|
|
var DEFAULT_COMPARATOR = comparators.DEFAULT_COMPARATOR,
|
|
reverseComparator = comparators.reverseComparator;
|
|
|
|
/**
|
|
* Fibonacci Heap.
|
|
*
|
|
* @constructor
|
|
*/
|
|
function FibonacciHeap(comparator) {
|
|
this.clear();
|
|
this.comparator = comparator || DEFAULT_COMPARATOR;
|
|
|
|
if (typeof this.comparator !== 'function')
|
|
throw new Error('mnemonist/FibonacciHeap.constructor: given comparator should be a function.');
|
|
}
|
|
|
|
/**
|
|
* Method used to clear the heap.
|
|
*
|
|
* @return {undefined}
|
|
*/
|
|
FibonacciHeap.prototype.clear = function() {
|
|
|
|
// Properties
|
|
this.root = null;
|
|
this.min = null;
|
|
this.size = 0;
|
|
};
|
|
|
|
/**
|
|
* Function used to create a node.
|
|
*
|
|
* @param {any} item - Target item.
|
|
* @return {object}
|
|
*/
|
|
function createNode(item) {
|
|
return {
|
|
item: item,
|
|
degree: 0
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Function used to merge the given node with the root list.
|
|
*
|
|
* @param {FibonacciHeap} heap - Target heap.
|
|
* @param {Node} node - Target node.
|
|
*/
|
|
function mergeWithRoot(heap, node) {
|
|
if (!heap.root) {
|
|
heap.root = node;
|
|
}
|
|
else {
|
|
node.right = heap.root.right;
|
|
node.left = heap.root;
|
|
heap.root.right.left = node;
|
|
heap.root.right = node;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Method used to push an item into the heap.
|
|
*
|
|
* @param {any} item - Item to push.
|
|
* @return {number}
|
|
*/
|
|
FibonacciHeap.prototype.push = function(item) {
|
|
var node = createNode(item);
|
|
node.left = node;
|
|
node.right = node;
|
|
mergeWithRoot(this, node);
|
|
|
|
if (!this.min || this.comparator(node.item, this.min.item) <= 0)
|
|
this.min = node;
|
|
|
|
return ++this.size;
|
|
};
|
|
|
|
/**
|
|
* Method used to get the "first" item of the heap.
|
|
*
|
|
* @return {any}
|
|
*/
|
|
FibonacciHeap.prototype.peek = function() {
|
|
return this.min ? this.min.item : undefined;
|
|
};
|
|
|
|
/**
|
|
* Function used to consume the given linked list.
|
|
*
|
|
* @param {Node} head - Head node.
|
|
* @param {array}
|
|
*/
|
|
function consumeLinkedList(head) {
|
|
var nodes = [],
|
|
node = head,
|
|
flag = false;
|
|
|
|
while (true) {
|
|
if (node === head && flag)
|
|
break;
|
|
else if (node === head)
|
|
flag = true;
|
|
|
|
nodes.push(node);
|
|
node = node.right;
|
|
}
|
|
|
|
return nodes;
|
|
}
|
|
|
|
/**
|
|
* Function used to remove the target node from the root list.
|
|
*
|
|
* @param {FibonacciHeap} heap - Target heap.
|
|
* @param {Node} node - Target node.
|
|
*/
|
|
function removeFromRoot(heap, node) {
|
|
if (heap.root === node)
|
|
heap.root = node.right;
|
|
node.left.right = node.right;
|
|
node.right.left = node.left;
|
|
}
|
|
|
|
/**
|
|
* Function used to merge the given node with the child list of a root node.
|
|
*
|
|
* @param {Node} parent - Parent node.
|
|
* @param {Node} node - Target node.
|
|
*/
|
|
function mergeWithChild(parent, node) {
|
|
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;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Function used to link one node to another in the root list.
|
|
*
|
|
* @param {FibonacciHeap} heap - Target heap.
|
|
* @param {Node} y - Y node.
|
|
* @param {Node} x - X node.
|
|
*/
|
|
function link(heap, y, x) {
|
|
removeFromRoot(heap, y);
|
|
y.left = y;
|
|
y.right = y;
|
|
mergeWithChild(x, y);
|
|
x.degree++;
|
|
y.parent = x;
|
|
}
|
|
|
|
/**
|
|
* Function used to consolidate the heap.
|
|
*
|
|
* @param {FibonacciHeap} heap - Target heap.
|
|
*/
|
|
function consolidate(heap) {
|
|
var A = new Array(heap.size),
|
|
nodes = consumeLinkedList(heap.root),
|
|
i, l, x, y, d, t;
|
|
|
|
for (i = 0, l = nodes.length; i < l; i++) {
|
|
x = nodes[i];
|
|
d = x.degree;
|
|
|
|
while (A[d]) {
|
|
y = A[d];
|
|
|
|
if (heap.comparator(x.item, y.item) > 0) {
|
|
t = x;
|
|
x = y;
|
|
y = t;
|
|
}
|
|
|
|
link(heap, y, x);
|
|
A[d] = null;
|
|
d++;
|
|
}
|
|
|
|
A[d] = x;
|
|
}
|
|
|
|
for (i = 0; i < heap.size; i++) {
|
|
if (A[i] && heap.comparator(A[i].item, heap.min.item) <= 0)
|
|
heap.min = A[i];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Method used to retrieve & remove the "first" item of the heap.
|
|
*
|
|
* @return {any}
|
|
*/
|
|
FibonacciHeap.prototype.pop = function() {
|
|
if (!this.size)
|
|
return undefined;
|
|
|
|
var z = this.min;
|
|
|
|
if (z.child) {
|
|
var nodes = consumeLinkedList(z.child),
|
|
node,
|
|
i,
|
|
l;
|
|
|
|
for (i = 0, l = nodes.length; i < l; i++) {
|
|
node = nodes[i];
|
|
|
|
mergeWithRoot(this, node);
|
|
delete node.parent;
|
|
}
|
|
}
|
|
|
|
removeFromRoot(this, z);
|
|
|
|
if (z === z.right) {
|
|
this.min = null;
|
|
this.root = null;
|
|
}
|
|
else {
|
|
this.min = z.right;
|
|
consolidate(this);
|
|
}
|
|
|
|
this.size--;
|
|
|
|
return z.item;
|
|
};
|
|
|
|
/**
|
|
* Convenience known methods.
|
|
*/
|
|
FibonacciHeap.prototype.inspect = function() {
|
|
var proxy = {
|
|
size: this.size
|
|
};
|
|
|
|
if (this.min && 'item' in this.min)
|
|
proxy.top = this.min.item;
|
|
|
|
// Trick so that node displays the name of the constructor
|
|
Object.defineProperty(proxy, 'constructor', {
|
|
value: FibonacciHeap,
|
|
enumerable: false
|
|
});
|
|
|
|
return proxy;
|
|
};
|
|
|
|
if (typeof Symbol !== 'undefined')
|
|
FibonacciHeap.prototype[Symbol.for('nodejs.util.inspect.custom')] = FibonacciHeap.prototype.inspect;
|
|
|
|
/**
|
|
* Fibonacci Maximum Heap.
|
|
*
|
|
* @constructor
|
|
*/
|
|
function MaxFibonacciHeap(comparator) {
|
|
this.clear();
|
|
this.comparator = comparator || DEFAULT_COMPARATOR;
|
|
|
|
if (typeof this.comparator !== 'function')
|
|
throw new Error('mnemonist/FibonacciHeap.constructor: given comparator should be a function.');
|
|
|
|
this.comparator = reverseComparator(this.comparator);
|
|
}
|
|
|
|
MaxFibonacciHeap.prototype = FibonacciHeap.prototype;
|
|
|
|
/**
|
|
* Static @.from function taking an arbitrary iterable & converting it into
|
|
* a heap.
|
|
*
|
|
* @param {Iterable} iterable - Target iterable.
|
|
* @param {function} comparator - Custom comparator function.
|
|
* @return {FibonacciHeap}
|
|
*/
|
|
FibonacciHeap.from = function(iterable, comparator) {
|
|
var heap = new FibonacciHeap(comparator);
|
|
|
|
forEach(iterable, function(value) {
|
|
heap.push(value);
|
|
});
|
|
|
|
return heap;
|
|
};
|
|
|
|
MaxFibonacciHeap.from = function(iterable, comparator) {
|
|
var heap = new MaxFibonacciHeap(comparator);
|
|
|
|
forEach(iterable, function(value) {
|
|
heap.push(value);
|
|
});
|
|
|
|
return heap;
|
|
};
|
|
|
|
/**
|
|
* Exporting.
|
|
*/
|
|
FibonacciHeap.MinFibonacciHeap = FibonacciHeap;
|
|
FibonacciHeap.MaxFibonacciHeap = MaxFibonacciHeap;
|
|
module.exports = FibonacciHeap;
|