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
			| 
											3 years ago
										 | /* 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; |