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;
|
||
|
};
|