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.
		
		
		
		
		
			
		
			
				
					
					
						
							210 lines
						
					
					
						
							4.9 KiB
						
					
					
				
			
		
		
	
	
							210 lines
						
					
					
						
							4.9 KiB
						
					
					
				| /**
 | |
|  * Mnemonist Fixed Reverse Heap
 | |
|  * =============================
 | |
|  *
 | |
|  * Static heap implementation with fixed capacity. It's a "reverse" heap
 | |
|  * because it stores the elements in reverse so we can replace the worst
 | |
|  * item in logarithmic time. As such, one cannot pop this heap but can only
 | |
|  * consume it at the end. This structure is very efficient when trying to
 | |
|  * find the n smallest/largest items from a larger query (k nearest neigbors
 | |
|  * for instance).
 | |
|  */
 | |
| var comparators = require('./utils/comparators.js'),
 | |
|     Heap = require('./heap.js');
 | |
| 
 | |
| var DEFAULT_COMPARATOR = comparators.DEFAULT_COMPARATOR,
 | |
|     reverseComparator = comparators.reverseComparator;
 | |
| 
 | |
| /**
 | |
|  * Helper functions.
 | |
|  */
 | |
| 
 | |
| /**
 | |
|  * Function used to sift up.
 | |
|  *
 | |
|  * @param {function} compare - Comparison function.
 | |
|  * @param {array}    heap    - Array storing the heap's data.
 | |
|  * @param {number}   size    - Heap's true size.
 | |
|  * @param {number}   i       - Index.
 | |
|  */
 | |
| function siftUp(compare, heap, size, i) {
 | |
|   var endIndex = size,
 | |
|       startIndex = i,
 | |
|       item = heap[i],
 | |
|       childIndex = 2 * i + 1,
 | |
|       rightIndex;
 | |
| 
 | |
|   while (childIndex < endIndex) {
 | |
|     rightIndex = childIndex + 1;
 | |
| 
 | |
|     if (
 | |
|       rightIndex < endIndex &&
 | |
|       compare(heap[childIndex], heap[rightIndex]) >= 0
 | |
|     ) {
 | |
|       childIndex = rightIndex;
 | |
|     }
 | |
| 
 | |
|     heap[i] = heap[childIndex];
 | |
|     i = childIndex;
 | |
|     childIndex = 2 * i + 1;
 | |
|   }
 | |
| 
 | |
|   heap[i] = item;
 | |
|   Heap.siftDown(compare, heap, startIndex, i);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Fully consumes the given heap.
 | |
|  *
 | |
|  * @param  {function} ArrayClass - Array class to use.
 | |
|  * @param  {function} compare    - Comparison function.
 | |
|  * @param  {array}    heap       - Array storing the heap's data.
 | |
|  * @param  {number}   size       - True size of the heap.
 | |
|  * @return {array}
 | |
|  */
 | |
| function consume(ArrayClass, compare, heap, size) {
 | |
|   var l = size,
 | |
|       i = l;
 | |
| 
 | |
|   var array = new ArrayClass(size),
 | |
|       lastItem,
 | |
|       item;
 | |
| 
 | |
|   while (i > 0) {
 | |
|     lastItem = heap[--i];
 | |
| 
 | |
|     if (i !== 0) {
 | |
|       item = heap[0];
 | |
|       heap[0] = lastItem;
 | |
|       siftUp(compare, heap, --size, 0);
 | |
|       lastItem = item;
 | |
|     }
 | |
| 
 | |
|     array[i] = lastItem;
 | |
|   }
 | |
| 
 | |
|   return array;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Binary Minimum FixedReverseHeap.
 | |
|  *
 | |
|  * @constructor
 | |
|  * @param {function} ArrayClass - The class of array to use.
 | |
|  * @param {function} comparator - Comparator function.
 | |
|  * @param {number}   capacity   - Maximum number of items to keep.
 | |
|  */
 | |
| function FixedReverseHeap(ArrayClass, comparator, capacity) {
 | |
| 
 | |
|   // Comparator can be omitted
 | |
|   if (arguments.length === 2) {
 | |
|     capacity = comparator;
 | |
|     comparator = null;
 | |
|   }
 | |
| 
 | |
|   this.ArrayClass = ArrayClass;
 | |
|   this.capacity = capacity;
 | |
| 
 | |
|   this.items = new ArrayClass(capacity);
 | |
|   this.clear();
 | |
|   this.comparator = comparator || DEFAULT_COMPARATOR;
 | |
| 
 | |
|   if (typeof capacity !== 'number' && capacity <= 0)
 | |
|     throw new Error('mnemonist/FixedReverseHeap.constructor: capacity should be a number > 0.');
 | |
| 
 | |
|   if (typeof this.comparator !== 'function')
 | |
|     throw new Error('mnemonist/FixedReverseHeap.constructor: given comparator should be a function.');
 | |
| 
 | |
|   this.comparator = reverseComparator(this.comparator);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Method used to clear the heap.
 | |
|  *
 | |
|  * @return {undefined}
 | |
|  */
 | |
| FixedReverseHeap.prototype.clear = function() {
 | |
| 
 | |
|   // Properties
 | |
|   this.size = 0;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Method used to push an item into the heap.
 | |
|  *
 | |
|  * @param  {any}    item - Item to push.
 | |
|  * @return {number}
 | |
|  */
 | |
| FixedReverseHeap.prototype.push = function(item) {
 | |
| 
 | |
|   // Still some place
 | |
|   if (this.size < this.capacity) {
 | |
|     this.items[this.size] = item;
 | |
|     Heap.siftDown(this.comparator, this.items, 0, this.size);
 | |
|     this.size++;
 | |
|   }
 | |
| 
 | |
|   // Heap is full, we need to replace worst item
 | |
|   else {
 | |
| 
 | |
|     if (this.comparator(item, this.items[0]) > 0)
 | |
|       Heap.replace(this.comparator, this.items, item);
 | |
|   }
 | |
| 
 | |
|   return this.size;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Method used to peek the worst item in the heap.
 | |
|  *
 | |
|  * @return {any}
 | |
|  */
 | |
| FixedReverseHeap.prototype.peek = function() {
 | |
|   return this.items[0];
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Method used to consume the heap fully and return its items as a sorted array.
 | |
|  *
 | |
|  * @return {array}
 | |
|  */
 | |
| FixedReverseHeap.prototype.consume = function() {
 | |
|   var items = consume(this.ArrayClass, this.comparator, this.items, this.size);
 | |
|   this.size = 0;
 | |
| 
 | |
|   return items;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Method used to convert the heap to an array. Note that it basically clone
 | |
|  * the heap and consumes it completely. This is hardly performant.
 | |
|  *
 | |
|  * @return {array}
 | |
|  */
 | |
| FixedReverseHeap.prototype.toArray = function() {
 | |
|   return consume(this.ArrayClass, this.comparator, this.items.slice(0, this.size), this.size);
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Convenience known methods.
 | |
|  */
 | |
| FixedReverseHeap.prototype.inspect = function() {
 | |
|   var proxy = this.toArray();
 | |
| 
 | |
|   // Trick so that node displays the name of the constructor
 | |
|   Object.defineProperty(proxy, 'constructor', {
 | |
|     value: FixedReverseHeap,
 | |
|     enumerable: false
 | |
|   });
 | |
| 
 | |
|   return proxy;
 | |
| };
 | |
| 
 | |
| if (typeof Symbol !== 'undefined')
 | |
|   FixedReverseHeap.prototype[Symbol.for('nodejs.util.inspect.custom')] = FixedReverseHeap.prototype.inspect;
 | |
| 
 | |
| /**
 | |
|  * Exporting.
 | |
|  */
 | |
| module.exports = FixedReverseHeap;
 |