Manual class transform
This commit is contained in:
@@ -4,6 +4,13 @@
|
||||
import {assert} from '../asserts.js';
|
||||
import {clear} from '../obj.js';
|
||||
|
||||
|
||||
/**
|
||||
* @type {number}
|
||||
*/
|
||||
export const DROP = Infinity;
|
||||
|
||||
|
||||
/**
|
||||
* Priority queue.
|
||||
*
|
||||
@@ -19,257 +26,256 @@ import {clear} from '../obj.js';
|
||||
* @struct
|
||||
* @template T
|
||||
*/
|
||||
const PriorityQueue = function(priorityFunction, keyFunction) {
|
||||
class PriorityQueue {
|
||||
|
||||
/**
|
||||
* @type {function(T): number}
|
||||
* @private
|
||||
*/
|
||||
this.priorityFunction_ = priorityFunction;
|
||||
constructor(priorityFunction, keyFunction) {
|
||||
|
||||
/**
|
||||
* @type {function(T): string}
|
||||
* @private
|
||||
*/
|
||||
this.keyFunction_ = keyFunction;
|
||||
/**
|
||||
* @type {function(T): number}
|
||||
* @private
|
||||
*/
|
||||
this.priorityFunction_ = priorityFunction;
|
||||
|
||||
/**
|
||||
* @type {Array.<T>}
|
||||
* @private
|
||||
*/
|
||||
this.elements_ = [];
|
||||
/**
|
||||
* @type {function(T): string}
|
||||
* @private
|
||||
*/
|
||||
this.keyFunction_ = keyFunction;
|
||||
|
||||
/**
|
||||
* @type {Array.<number>}
|
||||
* @private
|
||||
*/
|
||||
this.priorities_ = [];
|
||||
/**
|
||||
* @type {Array.<T>}
|
||||
* @private
|
||||
*/
|
||||
this.elements_ = [];
|
||||
|
||||
/**
|
||||
* @type {!Object.<string, boolean>}
|
||||
* @private
|
||||
*/
|
||||
this.queuedElements_ = {};
|
||||
/**
|
||||
* @type {Array.<number>}
|
||||
* @private
|
||||
*/
|
||||
this.priorities_ = [];
|
||||
|
||||
};
|
||||
/**
|
||||
* @type {!Object.<string, boolean>}
|
||||
* @private
|
||||
*/
|
||||
this.queuedElements_ = {};
|
||||
|
||||
|
||||
/**
|
||||
* @type {number}
|
||||
*/
|
||||
export const DROP = Infinity;
|
||||
|
||||
|
||||
/**
|
||||
* FIXME empty description for jsdoc
|
||||
*/
|
||||
PriorityQueue.prototype.clear = function() {
|
||||
this.elements_.length = 0;
|
||||
this.priorities_.length = 0;
|
||||
clear(this.queuedElements_);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Remove and return the highest-priority element. O(log N).
|
||||
* @return {T} Element.
|
||||
*/
|
||||
PriorityQueue.prototype.dequeue = function() {
|
||||
const elements = this.elements_;
|
||||
const priorities = this.priorities_;
|
||||
const element = elements[0];
|
||||
if (elements.length == 1) {
|
||||
elements.length = 0;
|
||||
priorities.length = 0;
|
||||
} else {
|
||||
elements[0] = elements.pop();
|
||||
priorities[0] = priorities.pop();
|
||||
this.siftUp_(0);
|
||||
}
|
||||
const elementKey = this.keyFunction_(element);
|
||||
delete this.queuedElements_[elementKey];
|
||||
return element;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Enqueue an element. O(log N).
|
||||
* @param {T} element Element.
|
||||
* @return {boolean} The element was added to the queue.
|
||||
*/
|
||||
PriorityQueue.prototype.enqueue = function(element) {
|
||||
assert(!(this.keyFunction_(element) in this.queuedElements_),
|
||||
31); // Tried to enqueue an `element` that was already added to the queue
|
||||
const priority = this.priorityFunction_(element);
|
||||
if (priority != DROP) {
|
||||
this.elements_.push(element);
|
||||
this.priorities_.push(priority);
|
||||
this.queuedElements_[this.keyFunction_(element)] = true;
|
||||
this.siftDown_(0, this.elements_.length - 1);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {number} Count.
|
||||
*/
|
||||
PriorityQueue.prototype.getCount = function() {
|
||||
return this.elements_.length;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets the index of the left child of the node at the given index.
|
||||
* @param {number} index The index of the node to get the left child for.
|
||||
* @return {number} The index of the left child.
|
||||
* @private
|
||||
*/
|
||||
PriorityQueue.prototype.getLeftChildIndex_ = function(index) {
|
||||
return index * 2 + 1;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets the index of the right child of the node at the given index.
|
||||
* @param {number} index The index of the node to get the right child for.
|
||||
* @return {number} The index of the right child.
|
||||
* @private
|
||||
*/
|
||||
PriorityQueue.prototype.getRightChildIndex_ = function(index) {
|
||||
return index * 2 + 2;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Gets the index of the parent of the node at the given index.
|
||||
* @param {number} index The index of the node to get the parent for.
|
||||
* @return {number} The index of the parent.
|
||||
* @private
|
||||
*/
|
||||
PriorityQueue.prototype.getParentIndex_ = function(index) {
|
||||
return (index - 1) >> 1;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Make this a heap. O(N).
|
||||
* @private
|
||||
*/
|
||||
PriorityQueue.prototype.heapify_ = function() {
|
||||
let i;
|
||||
for (i = (this.elements_.length >> 1) - 1; i >= 0; i--) {
|
||||
this.siftUp_(i);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {boolean} Is empty.
|
||||
*/
|
||||
PriorityQueue.prototype.isEmpty = function() {
|
||||
return this.elements_.length === 0;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {string} key Key.
|
||||
* @return {boolean} Is key queued.
|
||||
*/
|
||||
PriorityQueue.prototype.isKeyQueued = function(key) {
|
||||
return key in this.queuedElements_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {T} element Element.
|
||||
* @return {boolean} Is queued.
|
||||
*/
|
||||
PriorityQueue.prototype.isQueued = function(element) {
|
||||
return this.isKeyQueued(this.keyFunction_(element));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {number} index The index of the node to move down.
|
||||
* @private
|
||||
*/
|
||||
PriorityQueue.prototype.siftUp_ = function(index) {
|
||||
const elements = this.elements_;
|
||||
const priorities = this.priorities_;
|
||||
const count = elements.length;
|
||||
const element = elements[index];
|
||||
const priority = priorities[index];
|
||||
const startIndex = index;
|
||||
|
||||
while (index < (count >> 1)) {
|
||||
const lIndex = this.getLeftChildIndex_(index);
|
||||
const rIndex = this.getRightChildIndex_(index);
|
||||
|
||||
const smallerChildIndex = rIndex < count &&
|
||||
priorities[rIndex] < priorities[lIndex] ?
|
||||
rIndex : lIndex;
|
||||
|
||||
elements[index] = elements[smallerChildIndex];
|
||||
priorities[index] = priorities[smallerChildIndex];
|
||||
index = smallerChildIndex;
|
||||
}
|
||||
|
||||
elements[index] = element;
|
||||
priorities[index] = priority;
|
||||
this.siftDown_(startIndex, index);
|
||||
};
|
||||
/**
|
||||
* FIXME empty description for jsdoc
|
||||
*/
|
||||
clear() {
|
||||
this.elements_.length = 0;
|
||||
this.priorities_.length = 0;
|
||||
clear(this.queuedElements_);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param {number} startIndex The index of the root.
|
||||
* @param {number} index The index of the node to move up.
|
||||
* @private
|
||||
*/
|
||||
PriorityQueue.prototype.siftDown_ = function(startIndex, index) {
|
||||
const elements = this.elements_;
|
||||
const priorities = this.priorities_;
|
||||
const element = elements[index];
|
||||
const priority = priorities[index];
|
||||
|
||||
while (index > startIndex) {
|
||||
const parentIndex = this.getParentIndex_(index);
|
||||
if (priorities[parentIndex] > priority) {
|
||||
elements[index] = elements[parentIndex];
|
||||
priorities[index] = priorities[parentIndex];
|
||||
index = parentIndex;
|
||||
/**
|
||||
* Remove and return the highest-priority element. O(log N).
|
||||
* @return {T} Element.
|
||||
*/
|
||||
dequeue() {
|
||||
const elements = this.elements_;
|
||||
const priorities = this.priorities_;
|
||||
const element = elements[0];
|
||||
if (elements.length == 1) {
|
||||
elements.length = 0;
|
||||
priorities.length = 0;
|
||||
} else {
|
||||
break;
|
||||
elements[0] = elements.pop();
|
||||
priorities[0] = priorities.pop();
|
||||
this.siftUp_(0);
|
||||
}
|
||||
const elementKey = this.keyFunction_(element);
|
||||
delete this.queuedElements_[elementKey];
|
||||
return element;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Enqueue an element. O(log N).
|
||||
* @param {T} element Element.
|
||||
* @return {boolean} The element was added to the queue.
|
||||
*/
|
||||
enqueue(element) {
|
||||
assert(!(this.keyFunction_(element) in this.queuedElements_),
|
||||
31); // Tried to enqueue an `element` that was already added to the queue
|
||||
const priority = this.priorityFunction_(element);
|
||||
if (priority != DROP) {
|
||||
this.elements_.push(element);
|
||||
this.priorities_.push(priority);
|
||||
this.queuedElements_[this.keyFunction_(element)] = true;
|
||||
this.siftDown_(0, this.elements_.length - 1);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return {number} Count.
|
||||
*/
|
||||
getCount() {
|
||||
return this.elements_.length;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the index of the left child of the node at the given index.
|
||||
* @param {number} index The index of the node to get the left child for.
|
||||
* @return {number} The index of the left child.
|
||||
* @private
|
||||
*/
|
||||
getLeftChildIndex_(index) {
|
||||
return index * 2 + 1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the index of the right child of the node at the given index.
|
||||
* @param {number} index The index of the node to get the right child for.
|
||||
* @return {number} The index of the right child.
|
||||
* @private
|
||||
*/
|
||||
getRightChildIndex_(index) {
|
||||
return index * 2 + 2;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the index of the parent of the node at the given index.
|
||||
* @param {number} index The index of the node to get the parent for.
|
||||
* @return {number} The index of the parent.
|
||||
* @private
|
||||
*/
|
||||
getParentIndex_(index) {
|
||||
return (index - 1) >> 1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Make this a heap. O(N).
|
||||
* @private
|
||||
*/
|
||||
heapify_() {
|
||||
let i;
|
||||
for (i = (this.elements_.length >> 1) - 1; i >= 0; i--) {
|
||||
this.siftUp_(i);
|
||||
}
|
||||
}
|
||||
elements[index] = element;
|
||||
priorities[index] = priority;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* FIXME empty description for jsdoc
|
||||
*/
|
||||
PriorityQueue.prototype.reprioritize = function() {
|
||||
const priorityFunction = this.priorityFunction_;
|
||||
const elements = this.elements_;
|
||||
const priorities = this.priorities_;
|
||||
let index = 0;
|
||||
const n = elements.length;
|
||||
let element, i, priority;
|
||||
for (i = 0; i < n; ++i) {
|
||||
element = elements[i];
|
||||
priority = priorityFunction(element);
|
||||
if (priority == DROP) {
|
||||
delete this.queuedElements_[this.keyFunction_(element)];
|
||||
} else {
|
||||
priorities[index] = priority;
|
||||
elements[index++] = element;
|
||||
}
|
||||
/**
|
||||
* @return {boolean} Is empty.
|
||||
*/
|
||||
isEmpty() {
|
||||
return this.elements_.length === 0;
|
||||
}
|
||||
elements.length = index;
|
||||
priorities.length = index;
|
||||
this.heapify_();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {string} key Key.
|
||||
* @return {boolean} Is key queued.
|
||||
*/
|
||||
isKeyQueued(key) {
|
||||
return key in this.queuedElements_;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param {T} element Element.
|
||||
* @return {boolean} Is queued.
|
||||
*/
|
||||
isQueued(element) {
|
||||
return this.isKeyQueued(this.keyFunction_(element));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param {number} index The index of the node to move down.
|
||||
* @private
|
||||
*/
|
||||
siftUp_(index) {
|
||||
const elements = this.elements_;
|
||||
const priorities = this.priorities_;
|
||||
const count = elements.length;
|
||||
const element = elements[index];
|
||||
const priority = priorities[index];
|
||||
const startIndex = index;
|
||||
|
||||
while (index < (count >> 1)) {
|
||||
const lIndex = this.getLeftChildIndex_(index);
|
||||
const rIndex = this.getRightChildIndex_(index);
|
||||
|
||||
const smallerChildIndex = rIndex < count &&
|
||||
priorities[rIndex] < priorities[lIndex] ?
|
||||
rIndex : lIndex;
|
||||
|
||||
elements[index] = elements[smallerChildIndex];
|
||||
priorities[index] = priorities[smallerChildIndex];
|
||||
index = smallerChildIndex;
|
||||
}
|
||||
|
||||
elements[index] = element;
|
||||
priorities[index] = priority;
|
||||
this.siftDown_(startIndex, index);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param {number} startIndex The index of the root.
|
||||
* @param {number} index The index of the node to move up.
|
||||
* @private
|
||||
*/
|
||||
siftDown_(startIndex, index) {
|
||||
const elements = this.elements_;
|
||||
const priorities = this.priorities_;
|
||||
const element = elements[index];
|
||||
const priority = priorities[index];
|
||||
|
||||
while (index > startIndex) {
|
||||
const parentIndex = this.getParentIndex_(index);
|
||||
if (priorities[parentIndex] > priority) {
|
||||
elements[index] = elements[parentIndex];
|
||||
priorities[index] = priorities[parentIndex];
|
||||
index = parentIndex;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
elements[index] = element;
|
||||
priorities[index] = priority;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* FIXME empty description for jsdoc
|
||||
*/
|
||||
reprioritize() {
|
||||
const priorityFunction = this.priorityFunction_;
|
||||
const elements = this.elements_;
|
||||
const priorities = this.priorities_;
|
||||
let index = 0;
|
||||
const n = elements.length;
|
||||
let element, i, priority;
|
||||
for (i = 0; i < n; ++i) {
|
||||
element = elements[i];
|
||||
priority = priorityFunction(element);
|
||||
if (priority == DROP) {
|
||||
delete this.queuedElements_[this.keyFunction_(element)];
|
||||
} else {
|
||||
priorities[index] = priority;
|
||||
elements[index++] = element;
|
||||
}
|
||||
}
|
||||
elements.length = index;
|
||||
priorities.length = index;
|
||||
this.heapify_();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
export default PriorityQueue;
|
||||
|
||||
Reference in New Issue
Block a user