/** * FastPriorityQueue.js : a fast heap-based priority queue in JavaScript. * (c) the authors * Licensed under the Apache License, Version 2.0. * * Speed-optimized heap-based priority queue for modern browsers and JavaScript engines. * * Usage : Installation (in shell, if you use node): $ npm install fastpriorityqueue Running test program (in JavaScript): // var FastPriorityQueue = require("fastpriorityqueue");// in node var x = new FastPriorityQueue(); x.add(1); x.add(0); x.add(5); x.add(4); x.add(3); x.peek(); // should return 0, leaves x unchanged x.size; // should return 5, leaves x unchanged while(!x.isEmpty()) { console.log(x.poll()); } // will print 0 1 3 4 5 x.trim(); // (optional) optimizes memory usage */ 'use strict'; var defaultcomparator = function(a, b) { return a < b; }; // the provided comparator function should take a, b and return *true* when a < b function FastPriorityQueue(comparator) { if (!(this instanceof FastPriorityQueue)) return new FastPriorityQueue(comparator); this.array = []; this.size = 0; this.compare = comparator || defaultcomparator; } FastPriorityQueue.prototype.clone = function() { var fpq = new FastPriorityQueue(this.compare); fpq.size = this.size; for (var i = 0; i < this.size; i++) { fpq.array.push(this.array[i]); } return fpq; }; // Add an element into the queue // runs in O(log n) time FastPriorityQueue.prototype.add = function(myval) { var i = this.size; this.array[this.size] = myval; this.size += 1; var p; var ap; while (i > 0) { p = (i - 1) >> 1; ap = this.array[p]; if (!this.compare(myval, ap)) { break; } this.array[i] = ap; i = p; } this.array[i] = myval; }; // replace the content of the heap by provided array and "heapifies it" FastPriorityQueue.prototype.heapify = function(arr) { this.array = arr; this.size = arr.length; var i; for (i = this.size >> 1; i >= 0; i--) { this._percolateDown(i); } }; // for internal use FastPriorityQueue.prototype._percolateUp = function(i, force) { var myval = this.array[i]; var p; var ap; while (i > 0) { p = (i - 1) >> 1; ap = this.array[p]; // force will skip the compare if (!force && !this.compare(myval, ap)) { break; } this.array[i] = ap; i = p; } this.array[i] = myval; }; // for internal use FastPriorityQueue.prototype._percolateDown = function(i) { var size = this.size; var hsize = this.size >>> 1; var ai = this.array[i]; var l; var r; var bestc; while (i < hsize) { l = (i << 1) + 1; r = l + 1; bestc = this.array[l]; if (r < size) { if (this.compare(this.array[r], bestc)) { l = r; bestc = this.array[r]; } } if (!this.compare(bestc, ai)) { break; } this.array[i] = bestc; i = l; } this.array[i] = ai; }; // internal // _removeAt(index) will delete the given index from the queue, // retaining balance. returns true if removed. FastPriorityQueue.prototype._removeAt = function(index) { if (this.isEmpty() || index > this.size - 1 || index < 0) return false; // impl1: //this.array.splice(index, 1); //this.heapify(this.array); // impl2: this._percolateUp(index, true); this.poll(); return true; }; // remove(myval[, comparator]) will remove the given item from the // queue, checked for equality by using compare if a new comparator isn't provided. // (for exmaple, if you want to remove based on a seperate key value, not necessarily priority). // return true if removed. FastPriorityQueue.prototype.remove = function(myval, comparator) { if (!comparator) { comparator = this.compare; } if (this.isEmpty()) return false; for (var i = 0; i < this.size; i++) { if (comparator(this.array[i], myval) || comparator(myval, this.array[i])) { continue; } // items are equal, remove return this._removeAt(i); } return false; }; // Look at the top of the queue (a smallest element) // executes in constant time // // Calling peek on an empty priority queue returns // the "undefined" value. // https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/undefined // FastPriorityQueue.prototype.peek = function() { if (this.size == 0) return undefined; return this.array[0]; }; // remove the element on top of the heap (a smallest element) // runs in logarithmic time // // If the priority queue is empty, the function returns the // "undefined" value. // https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/undefined // // For long-running and large priority queues, or priority queues // storing large objects, you may want to call the trim function // at strategic times to recover allocated memory. FastPriorityQueue.prototype.poll = function() { if (this.size == 0) return undefined; var ans = this.array[0]; if (this.size > 1) { this.array[0] = this.array[--this.size]; this._percolateDown(0 | 0); } else { this.size -= 1; } return ans; }; // This function adds the provided value to the heap, while removing // and returning the peek value (like poll). The size of the priority // thus remains unchanged. FastPriorityQueue.prototype.replaceTop = function(myval) { if (this.size == 0) return undefined; var ans = this.array[0]; this.array[0] = myval; this._percolateDown(0 | 0); return ans; }; // recover unused memory (for long-running priority queues) FastPriorityQueue.prototype.trim = function() { this.array = this.array.slice(0, this.size); }; // Check whether the heap is empty FastPriorityQueue.prototype.isEmpty = function() { return this.size === 0; }; // iterate over the items in order, pass a callback that receives (item, index) as args. // TODO once we transpile, uncomment // if (Symbol && Symbol.iterator) { // FastPriorityQueue.prototype[Symbol.iterator] = function*() { // if (this.isEmpty()) return; // var fpq = this.clone(); // while (!fpq.isEmpty()) { // yield fpq.poll(); // } // }; // } FastPriorityQueue.prototype.forEach = function(callback) { if (this.isEmpty() || typeof callback != 'function') return; var i = 0; var fpq = this.clone(); while (!fpq.isEmpty()) { callback(fpq.poll(), i++); } }; // return the k 'smallest' elements of the queue // runs in O(k log k) time // this is the equivalent of repeatedly calling poll, but // it has a better computational complexity, which can be // important for large data sets. FastPriorityQueue.prototype.kSmallest = function(k) { if (this.size == 0) return []; var comparator = this.compare; var arr = this.array var fpq = new FastPriorityQueue(function(a,b){ return comparator(arr[a],arr[b]); }); k = Math.min(this.size, k); var smallest = new Array(k); var j = 0; fpq.add(0); while (j < k) { var small = fpq.poll(); smallest[j++] = this.array[small]; var l = (small << 1) + 1; var r = l + 1; if (l < this.size) fpq.add(l); if (r < this.size) fpq.add(r); } return smallest; }