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