Skip to content

Commit 8b5247a

Browse files
ref: KeyPriorityQueue in separate file TheAlgorithms#1298
1 parent 6aa3314 commit 8b5247a

File tree

1 file changed

+185
-0
lines changed

1 file changed

+185
-0
lines changed
Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
/**
2+
* KeyPriorityQueue is a priority queue based on a Minimum Binary Heap.
3+
*
4+
* Minimum Binary Heaps are binary trees which are filled level by level
5+
* and then from left to right inside a depth level.
6+
* Their main property is that any parent node has a smaller or equal priority to all of its children,
7+
* hence the root of the tree always has the smallest priority of all nodes.
8+
*
9+
* This implementation of the Minimum Binary Heap allows for nodes to contain both a key and a priority.
10+
* WARNING: keys must be Integers as they are used as array indices.
11+
*
12+
* In this implementation, the heap is represented by an array with nodes ordered
13+
* from root-to-leaf, left-to-right.
14+
* Therefore, the parent-child node relationship is such that
15+
* * the children nodes positions relative to their parent are: (parentPos * 2 + 1) and (parentPos * 2 + 2)
16+
* * the parent node position relative to either of its children is: Math.floor((childPos - 1) / 2)
17+
*
18+
* More information and visuals on Binary Heaps can be found here: https://www.geeksforgeeks.org/binary-heap/
19+
*/
20+
21+
22+
// Priority Queue Helper functions
23+
function getParentPosition(position) {
24+
// Get the parent node of the current node
25+
return Math.floor((position - 1) / 2)
26+
}
27+
function getChildrenPosition(position) {
28+
// Get the children nodes of the current node
29+
return [2 * position + 1, 2 * position + 2]
30+
}
31+
32+
class KeyPriorityQueue {
33+
// Priority Queue class using Minimum Binary Heap
34+
constructor() {
35+
this._heap = []
36+
this.keys = {}
37+
}
38+
39+
/**
40+
* Checks if the heap is empty
41+
* @returns boolean
42+
*/
43+
isEmpty() {
44+
return this._heap.length === 0
45+
}
46+
47+
/**
48+
* Adds an element to the queue
49+
* @param {number} key
50+
* @param {number} priority
51+
*/
52+
push(key, priority) {
53+
this._heap.push([key, priority])
54+
this.keys[key] = this._heap.length - 1
55+
this._shiftUp(this.keys[key])
56+
}
57+
58+
/**
59+
* Removes the element with least priority
60+
* @returns the key of the element with least priority
61+
*/
62+
pop() {
63+
this._swap(0, this._heap.length - 1)
64+
const [key] = this._heap.pop()
65+
delete this.keys[key]
66+
this._shiftDown(0)
67+
return key
68+
}
69+
70+
/**
71+
* Checks whether a given key is present in the queue
72+
* @param {number} key
73+
* @returns boolean
74+
*/
75+
contains(key) {
76+
return (key in this.keys)
77+
}
78+
79+
/**
80+
* Updates the priority of the given element
81+
* @param {number} key the element to change
82+
* @param {number} priority new priority of the element
83+
*/
84+
update(key, priority) {
85+
const currPos = this.keys[key]
86+
this._heap[currPos][1] = priority
87+
const parentPos = getParentPosition(currPos)
88+
const currPriority = this._heap[currPos][1]
89+
let parentPriority = Infinity
90+
if (parentPos >= 0) {
91+
parentPriority = this._heap[parentPos][1]
92+
}
93+
const [child1Pos, child2Pos] = getChildrenPosition(currPos)
94+
let [child1Priority, child2Priority] = [Infinity, Infinity]
95+
if (child1Pos < this._heap.length) {
96+
child1Priority = this._heap[child1Pos][1]
97+
}
98+
if (child2Pos < this._heap.length) {
99+
child2Priority = this._heap[child2Pos][1]
100+
}
101+
102+
if (parentPos >= 0 && parentPriority > currPriority) {
103+
this._shiftUp(currPos)
104+
} else if (child2Pos < this._heap.length &&
105+
(child1Priority < currPriority || child2Priority < currPriority)) {
106+
this._shiftDown(currPos)
107+
}
108+
}
109+
110+
_shiftUp(position) {
111+
// Helper function to shift up a node to proper position (equivalent to bubbleUp)
112+
let currPos = position
113+
let parentPos = getParentPosition(currPos)
114+
let currPriority = this._heap[currPos][1]
115+
let parentPriority = Infinity
116+
if (parentPos >= 0) {
117+
parentPriority = this._heap[parentPos][1]
118+
}
119+
120+
while (parentPos >= 0 && parentPriority > currPriority) {
121+
this._swap(currPos, parentPos)
122+
currPos = parentPos
123+
parentPos = getParentPosition(currPos)
124+
currPriority = this._heap[currPos][1]
125+
try {
126+
parentPriority = this._heap[parentPos][1]
127+
} catch (error) {
128+
parentPriority = Infinity
129+
}
130+
}
131+
this.keys[this._heap[currPos][0]] = currPos
132+
}
133+
134+
_shiftDown(position) {
135+
// Helper function to shift down a node to proper position (equivalent to bubbleDown)
136+
let currPos = position
137+
let [child1Pos, child2Pos] = getChildrenPosition(currPos)
138+
let [child1Priority, child2Priority] = [Infinity, Infinity]
139+
if (child1Pos < this._heap.length) {
140+
child1Priority = this._heap[child1Pos][1]
141+
}
142+
if (child2Pos < this._heap.length) {
143+
child2Priority = this._heap[child2Pos][1]
144+
}
145+
let currPriority
146+
try {
147+
currPriority = this._heap[currPos][1]
148+
} catch {
149+
return
150+
}
151+
152+
while (child2Pos < this._heap.length &&
153+
(child1Priority < currPriority || child2Priority < currPriority)) {
154+
if (child1Priority < currPriority && child1Priority < child2Priority) {
155+
this._swap(child1Pos, currPos)
156+
currPos = child1Pos
157+
} else {
158+
this._swap(child2Pos, currPos)
159+
currPos = child2Pos
160+
}
161+
[child1Pos, child2Pos] = getChildrenPosition(currPos)
162+
try {
163+
[child1Priority, child2Priority] = [this._heap[child1Pos][1], this._heap[child2Pos][1]]
164+
} catch (error) {
165+
[child1Priority, child2Priority] = [Infinity, Infinity]
166+
}
167+
168+
currPriority = this._heap[currPos][1]
169+
}
170+
this.keys[this._heap[currPos][0]] = currPos
171+
if (child1Pos < this._heap.length && child1Priority < currPriority) {
172+
this._swap(child1Pos, currPos)
173+
this.keys[this._heap[child1Pos][0]] = child1Pos
174+
}
175+
}
176+
177+
_swap(position1, position2) {
178+
// Helper function to swap 2 nodes
179+
[this._heap[position1], this._heap[position2]] = [this._heap[position2], this._heap[position1]]
180+
this.keys[this._heap[position1][0]] = position1
181+
this.keys[this._heap[position2][0]] = position2
182+
}
183+
}
184+
185+
export { KeyPriorityQueue }

0 commit comments

Comments
 (0)