mirror of
https://github.com/TheAlgorithms/JavaScript.git
synced 2025-07-05 00:01:37 +08:00

* chore: Switch to Node 20 + Vitest * chore: migrate to vitest mock functions * chore: code style (switch to prettier) * test: re-enable long-running test Seems the switch to Node 20 and Vitest has vastly improved the code's and / or the test's runtime! see #1193 * chore: code style * chore: fix failing tests * Updated Documentation in README.md * Update contribution guidelines to state usage of Prettier * fix: set prettier printWidth back to 80 * chore: apply updated code style automatically * fix: set prettier line endings to lf again * chore: apply updated code style automatically --------- Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Co-authored-by: Lars Müller <34514239+appgurueu@users.noreply.github.com>
158 lines
5.2 KiB
JavaScript
158 lines
5.2 KiB
JavaScript
/**
|
|
* KeyPriorityQueue is a priority queue based on a Minimum Binary Heap.
|
|
*
|
|
* Minimum Binary Heaps are binary trees which are filled level by level
|
|
* and then from left to right inside a depth level.
|
|
* Their main property is that any parent node has a smaller or equal priority to all of its children,
|
|
* hence the root of the tree always has the smallest priority of all nodes.
|
|
*
|
|
* This implementation of the Minimum Binary Heap allows for nodes to be associated to both a key,
|
|
* which can be any datatype, and a priority.
|
|
*
|
|
* The heap is represented by an array with nodes ordered
|
|
* from root-to-leaf, left-to-right.
|
|
* Therefore, the parent-child node relationship is such that
|
|
* * the children nodes positions relative to their parent are: (parentPos * 2 + 1) and (parentPos * 2 + 2)
|
|
* * the parent node position relative to either of its children is: Math.floor((childPos - 1) / 2)
|
|
*
|
|
* More information and visuals on Binary Heaps can be found here: https://www.geeksforgeeks.org/binary-heap/
|
|
*/
|
|
|
|
// Priority Queue Helper functions
|
|
const getParentPosition = (position) => Math.floor((position - 1) / 2)
|
|
const getChildrenPositions = (position) => [2 * position + 1, 2 * position + 2]
|
|
|
|
class KeyPriorityQueue {
|
|
// Priority Queue class using Minimum Binary Heap
|
|
constructor() {
|
|
this._heap = []
|
|
this.priorities = new Map()
|
|
}
|
|
|
|
/**
|
|
* Checks if the heap is empty
|
|
* @returns boolean
|
|
*/
|
|
isEmpty() {
|
|
return this._heap.length === 0
|
|
}
|
|
|
|
/**
|
|
* Adds an element to the queue
|
|
* @param {*} key
|
|
* @param {number} priority
|
|
*/
|
|
push(key, priority) {
|
|
this._heap.push(key)
|
|
this.priorities.set(key, priority)
|
|
this._shiftUp(this._heap.length - 1)
|
|
}
|
|
|
|
/**
|
|
* Removes the element with least priority
|
|
* @returns the key of the element with least priority
|
|
*/
|
|
pop() {
|
|
this._swap(0, this._heap.length - 1)
|
|
const key = this._heap.pop()
|
|
this.priorities.delete(key)
|
|
this._shiftDown(0)
|
|
return key
|
|
}
|
|
|
|
/**
|
|
* Checks whether a given key is present in the queue
|
|
* @param {*} key
|
|
* @returns boolean
|
|
*/
|
|
contains(key) {
|
|
return this.priorities.has(key)
|
|
}
|
|
|
|
/**
|
|
* Updates the priority of the given element.
|
|
* Adds the element if it is not in the queue.
|
|
* @param {*} key the element to change
|
|
* @param {number} priority new priority of the element
|
|
*/
|
|
update(key, priority) {
|
|
const currPos = this._heap.indexOf(key)
|
|
// if the key does not exist yet, add it
|
|
if (currPos === -1) return this.push(key, priority)
|
|
// else update priority
|
|
this.priorities.set(key, priority)
|
|
const parentPos = getParentPosition(currPos)
|
|
const currPriority = this._getPriorityOrInfinite(currPos)
|
|
const parentPriority = this._getPriorityOrInfinite(parentPos)
|
|
const [child1Pos, child2Pos] = getChildrenPositions(currPos)
|
|
const child1Priority = this._getPriorityOrInfinite(child1Pos)
|
|
const child2Priority = this._getPriorityOrInfinite(child2Pos)
|
|
|
|
if (parentPos >= 0 && parentPriority > currPriority) {
|
|
this._shiftUp(currPos)
|
|
} else if (child1Priority < currPriority || child2Priority < currPriority) {
|
|
this._shiftDown(currPos)
|
|
}
|
|
}
|
|
|
|
_getPriorityOrInfinite(position) {
|
|
// Helper function, returns priority of the node, or Infinite if no node corresponds to this position
|
|
if (position >= 0 && position < this._heap.length)
|
|
return this.priorities.get(this._heap[position])
|
|
else return Infinity
|
|
}
|
|
|
|
_shiftUp(position) {
|
|
// Helper function to shift up a node to proper position (equivalent to bubbleUp)
|
|
let currPos = position
|
|
let parentPos = getParentPosition(currPos)
|
|
let currPriority = this._getPriorityOrInfinite(currPos)
|
|
let parentPriority = this._getPriorityOrInfinite(parentPos)
|
|
|
|
while (parentPos >= 0 && parentPriority > currPriority) {
|
|
this._swap(currPos, parentPos)
|
|
currPos = parentPos
|
|
parentPos = getParentPosition(currPos)
|
|
currPriority = this._getPriorityOrInfinite(currPos)
|
|
parentPriority = this._getPriorityOrInfinite(parentPos)
|
|
}
|
|
}
|
|
|
|
_shiftDown(position) {
|
|
// Helper function to shift down a node to proper position (equivalent to bubbleDown)
|
|
let currPos = position
|
|
let [child1Pos, child2Pos] = getChildrenPositions(currPos)
|
|
let child1Priority = this._getPriorityOrInfinite(child1Pos)
|
|
let child2Priority = this._getPriorityOrInfinite(child2Pos)
|
|
let currPriority = this._getPriorityOrInfinite(currPos)
|
|
|
|
if (currPriority === Infinity) {
|
|
return
|
|
}
|
|
|
|
while (child1Priority < currPriority || child2Priority < currPriority) {
|
|
if (child1Priority < currPriority && child1Priority < child2Priority) {
|
|
this._swap(child1Pos, currPos)
|
|
currPos = child1Pos
|
|
} else {
|
|
this._swap(child2Pos, currPos)
|
|
currPos = child2Pos
|
|
}
|
|
;[child1Pos, child2Pos] = getChildrenPositions(currPos)
|
|
child1Priority = this._getPriorityOrInfinite(child1Pos)
|
|
child2Priority = this._getPriorityOrInfinite(child2Pos)
|
|
currPriority = this._getPriorityOrInfinite(currPos)
|
|
}
|
|
}
|
|
|
|
_swap(position1, position2) {
|
|
// Helper function to swap 2 nodes
|
|
;[this._heap[position1], this._heap[position2]] = [
|
|
this._heap[position2],
|
|
this._heap[position1]
|
|
]
|
|
}
|
|
}
|
|
|
|
export { KeyPriorityQueue }
|