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