es-next-tools
Version:
A comprehensive utility library for JavaScript and TypeScript that provides a wide range of functions for common programming tasks, including mathematical operations, date manipulations, array and object handling, string utilities, and more.
167 lines (166 loc) • 6.02 kB
JavaScript
/**
* Represents a priority queue data structure.
* @template T The type of elements in the priority queue.
* @example
* const pq = new PriorityQueue<string>();
* pq.enqueue('task1', 2).enqueue('task2', 1);
* console.log(pq.dequeue()); // 'task2'
*/
export class PriorityQueue {
items = [];
/**
* Adds an item to the priority queue with the specified priority.
* @param {T} item - The item to add to the priority queue.
* @param {number} priority - The priority of the item (lower numbers indicate higher priority). Default: 1
* @returns {PriorityQueue<T>} The priority queue instance for chaining.
*/
enqueue(item, priority = 1) {
const newItem = { value: item, priority };
this.items.push(newItem);
this.bubbleUp();
return this;
}
bubbleUp() {
let index = this.items.length - 1;
while (index > 0) {
const parentIndex = (index - 1) / 2 | 0;
if (this.items[index].priority >= this.items[parentIndex].priority)
break;
[this.items[index], this.items[parentIndex]] = [this.items[parentIndex], this.items[index]];
index = parentIndex;
}
}
/**
* Removes and returns the item with the highest priority from the queue.
* @returns {T} The item with the highest priority, or undefined if the queue is empty.
* @throws {Error} If the priority queue is empty.
*/
dequeue() {
if (this.isEmpty())
throw new Error('Priority queue is empty');
const top = this.items[0];
const bottom = this.items.pop();
if (this.items.length > 0 && bottom) {
this.items[0] = bottom;
this.bubbleDown();
}
return top.value;
}
;
bubbleDown() {
let index = 0;
const length = this.items.length;
const element = this.items[0];
while (true) {
let leftChildIndex = 2 * index + 1;
let rightChildIndex = 2 * index + 2;
let swap = null;
if (leftChildIndex < length) {
const leftChild = this.items[leftChildIndex];
if (leftChild.priority < element.priority) {
swap = leftChildIndex;
}
}
if (rightChildIndex < length) {
const rightChild = this.items[rightChildIndex];
if ((swap === null && rightChild.priority < element.priority) ||
(swap !== null && rightChild.priority < this.items[swap].priority)) {
swap = rightChildIndex;
}
}
if (swap === null)
break;
this.items[index] = this.items[swap];
this.items[swap] = element;
index = swap;
}
}
/**
* Returns the number of items in the priority queue.
* @returns {number} The size of the priority queue.
*/
size() {
return this.items.length;
}
/**
* Checks if the priority queue is empty.
* @returns {boolean} True if the priority queue is empty, false otherwise.
*/
isEmpty() {
return this.items.length === 0;
}
/**
* Returns the item with the highest priority without removing it.
* @returns {T | undefined} The item with the highest priority, or undefined if the queue is empty.
*/
peek() {
return this.isEmpty() ? undefined : this.items[0].value;
}
/**
* Checks if the priority queue contains a specific item.
* @param {T} item - The item to check for.
* @returns {boolean} True if the item is in the priority queue, false otherwise.
*/
contains(item) {
let left = 0;
let right = this.items.length - 1;
while (left <= right) {
const mid = ~~((left + right) / 2);
const midValue = this.items[mid].value;
if (midValue === item)
return true; // Element found.
else if (midValue < item)
left = mid + 1; // Search in the right half.
else
right = mid - 1; // Search in the left half
}
return false; // Element not found.
}
/**
* Changes the priority of an existing item.
* @param {T} item - The item whose priority is to be changed.
* @param {number} newPriority - The new priority for the item.
* @returns {boolean} True if the priority was changed, false if the item was not found.
*/
changePriority(item, newPriority) {
const index = this.items.findIndex(i => i.value === item);
if (index === -1)
return false; // Item not found.
const oldItem = this.items[index];
this.items.splice(index, 1); // Remove the item.
this.enqueue(oldItem.value, newPriority); // Reinsert with new priority.
return true;
}
/**
* Removes a specific item from the priority queue.
* @param {T} item - The item to remove.
* @returns {T | boolean} The Value of the removed item if the item was removed, false otherwise.
*/
remove(item) {
const index = this.items.findIndex(i => i.value === item);
if (index === -1)
return false;
return this.items.splice(index, 1)[0].value;
}
/**
* Merges another priority queue into this one.
* @param {PriorityQueue<T>} otherQueue - The other priority queue to merge.
*/
merge(otherQueue) {
otherQueue.items.forEach(item => this.enqueue(item.value, item.priority));
}
/**
* Serializes the priority queue to a JSON string.
* @returns {string} The serialized priority queue.
*/
serialize() {
return JSON.stringify(this.items);
}
/**
* Deserializes a JSON string to populate the priority queue.
* @param {string} data - The JSON string to deserialize.
*/
deserialize(data) {
this.items = JSON.parse(data);
}
}