forked from TheAlgorithms/JavaScript
-
Notifications
You must be signed in to change notification settings - Fork 0
/
KeyPriorityQueue.js
157 lines (141 loc) · 5.2 KB
/
KeyPriorityQueue.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
/**
* 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 }