heli-agri
Version:
HeliAgri is a high-performance, feature-packed library for creating interactive maps on the web. It can display map tiles, vector data and markers loaded from any source on any web page. OpenLayers has been developed to further the use of geographic infor
263 lines (236 loc) • 6.61 kB
JavaScript
/**
* @module ol/structs/PriorityQueue
*/
import {assert} from '../asserts.js';
import {clear} from '../obj.js';
/**
* @type {number}
*/
export const DROP = Infinity;
/**
* @classdesc
* Priority queue.
*
* The implementation is inspired from the Closure Library's Heap class and
* Python's heapq module.
*
* See https://github.com/google/closure-library/blob/master/closure/goog/structs/heap.js
* and https://hg.python.org/cpython/file/2.7/Lib/heapq.py.
*
* @template T
*/
class PriorityQueue {
/**
* @param {function(T): number} priorityFunction Priority function.
* @param {function(T): string} keyFunction Key function.
*/
constructor(priorityFunction, keyFunction) {
/**
* @type {function(T): number}
* @private
*/
this.priorityFunction_ = priorityFunction;
/**
* @type {function(T): string}
* @private
*/
this.keyFunction_ = keyFunction;
/**
* @type {Array<T>}
* @private
*/
this.elements_ = [];
/**
* @type {Array<number>}
* @private
*/
this.priorities_ = [];
/**
* @type {!Object<string, boolean>}
* @private
*/
this.queuedElements_ = {};
}
/**
* FIXME empty description for jsdoc
*/
clear() {
this.elements_.length = 0;
this.priorities_.length = 0;
clear(this.queuedElements_);
}
/**
* 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 {
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);
}
}
/**
* @return {boolean} Is empty.
*/
isEmpty() {
return this.elements_.length === 0;
}
/**
* @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;