153 lines
		
	
	
		
			4.1 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			153 lines
		
	
	
		
			4.1 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| var _ = require("../lodash");
 | |
| 
 | |
| module.exports = PriorityQueue;
 | |
| 
 | |
| /**
 | |
|  * A min-priority queue data structure. This algorithm is derived from Cormen,
 | |
|  * et al., "Introduction to Algorithms". The basic idea of a min-priority
 | |
|  * queue is that you can efficiently (in O(1) time) get the smallest key in
 | |
|  * the queue. Adding and removing elements takes O(log n) time. A key can
 | |
|  * have its priority decreased in O(log n) time.
 | |
|  */
 | |
| function PriorityQueue() {
 | |
|   this._arr = [];
 | |
|   this._keyIndices = {};
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * Returns the number of elements in the queue. Takes `O(1)` time.
 | |
|  */
 | |
| PriorityQueue.prototype.size = function() {
 | |
|   return this._arr.length;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Returns the keys that are in the queue. Takes `O(n)` time.
 | |
|  */
 | |
| PriorityQueue.prototype.keys = function() {
 | |
|   return this._arr.map(function(x) { return x.key; });
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Returns `true` if **key** is in the queue and `false` if not.
 | |
|  */
 | |
| PriorityQueue.prototype.has = function(key) {
 | |
|   return _.has(this._keyIndices, key);
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Returns the priority for **key**. If **key** is not present in the queue
 | |
|  * then this function returns `undefined`. Takes `O(1)` time.
 | |
|  *
 | |
|  * @param {Object} key
 | |
|  */
 | |
| PriorityQueue.prototype.priority = function(key) {
 | |
|   var index = this._keyIndices[key];
 | |
|   if (index !== undefined) {
 | |
|     return this._arr[index].priority;
 | |
|   }
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Returns the key for the minimum element in this queue. If the queue is
 | |
|  * empty this function throws an Error. Takes `O(1)` time.
 | |
|  */
 | |
| PriorityQueue.prototype.min = function() {
 | |
|   if (this.size() === 0) {
 | |
|     throw new Error("Queue underflow");
 | |
|   }
 | |
|   return this._arr[0].key;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Inserts a new key into the priority queue. If the key already exists in
 | |
|  * the queue this function returns `false`; otherwise it will return `true`.
 | |
|  * Takes `O(n)` time.
 | |
|  *
 | |
|  * @param {Object} key the key to add
 | |
|  * @param {Number} priority the initial priority for the key
 | |
|  */
 | |
| PriorityQueue.prototype.add = function(key, priority) {
 | |
|   var keyIndices = this._keyIndices;
 | |
|   key = String(key);
 | |
|   if (!_.has(keyIndices, key)) {
 | |
|     var arr = this._arr;
 | |
|     var index = arr.length;
 | |
|     keyIndices[key] = index;
 | |
|     arr.push({key: key, priority: priority});
 | |
|     this._decrease(index);
 | |
|     return true;
 | |
|   }
 | |
|   return false;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Removes and returns the smallest key in the queue. Takes `O(log n)` time.
 | |
|  */
 | |
| PriorityQueue.prototype.removeMin = function() {
 | |
|   this._swap(0, this._arr.length - 1);
 | |
|   var min = this._arr.pop();
 | |
|   delete this._keyIndices[min.key];
 | |
|   this._heapify(0);
 | |
|   return min.key;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Decreases the priority for **key** to **priority**. If the new priority is
 | |
|  * greater than the previous priority, this function will throw an Error.
 | |
|  *
 | |
|  * @param {Object} key the key for which to raise priority
 | |
|  * @param {Number} priority the new priority for the key
 | |
|  */
 | |
| PriorityQueue.prototype.decrease = function(key, priority) {
 | |
|   var index = this._keyIndices[key];
 | |
|   if (priority > this._arr[index].priority) {
 | |
|     throw new Error("New priority is greater than current priority. " +
 | |
|         "Key: " + key + " Old: " + this._arr[index].priority + " New: " + priority);
 | |
|   }
 | |
|   this._arr[index].priority = priority;
 | |
|   this._decrease(index);
 | |
| };
 | |
| 
 | |
| PriorityQueue.prototype._heapify = function(i) {
 | |
|   var arr = this._arr;
 | |
|   var l = 2 * i;
 | |
|   var r = l + 1;
 | |
|   var largest = i;
 | |
|   if (l < arr.length) {
 | |
|     largest = arr[l].priority < arr[largest].priority ? l : largest;
 | |
|     if (r < arr.length) {
 | |
|       largest = arr[r].priority < arr[largest].priority ? r : largest;
 | |
|     }
 | |
|     if (largest !== i) {
 | |
|       this._swap(i, largest);
 | |
|       this._heapify(largest);
 | |
|     }
 | |
|   }
 | |
| };
 | |
| 
 | |
| PriorityQueue.prototype._decrease = function(index) {
 | |
|   var arr = this._arr;
 | |
|   var priority = arr[index].priority;
 | |
|   var parent;
 | |
|   while (index !== 0) {
 | |
|     parent = index >> 1;
 | |
|     if (arr[parent].priority < priority) {
 | |
|       break;
 | |
|     }
 | |
|     this._swap(index, parent);
 | |
|     index = parent;
 | |
|   }
 | |
| };
 | |
| 
 | |
| PriorityQueue.prototype._swap = function(i, j) {
 | |
|   var arr = this._arr;
 | |
|   var keyIndices = this._keyIndices;
 | |
|   var origArrI = arr[i];
 | |
|   var origArrJ = arr[j];
 | |
|   arr[i] = origArrJ;
 | |
|   arr[j] = origArrI;
 | |
|   keyIndices[origArrJ.key] = i;
 | |
|   keyIndices[origArrI.key] = j;
 | |
| };
 |