Skip to content

Commit 566d910

Browse files
fix: refactor PrimMST and fix bug in PriorityQueue (TheAlgorithms#1300)
* ref: KeyPriorityQueue in separate file TheAlgorithms#1298 * feat: add tests for KeyPriorityQueue TheAlgorithms#1298 * fix: _shiftDown refactored and corrected TheAlgorithms#1298 * fix: use KeyPriorityQueue in PrimMST TheAlgorithms#1298 * feat: add test for PrimMST TheAlgorithms#1298 * fix: format files TheAlgorithms#1298 * fix: minor coding style changes * fix: use map for keys and priorities TheAlgorithms#1298
1 parent 0c42758 commit 566d910

File tree

4 files changed

+301
-154
lines changed

4 files changed

+301
-154
lines changed
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
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 be associated to both a key,
10+
* which can be any datatype, and a priority.
11+
*
12+
* 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+
// Priority Queue Helper functions
22+
const getParentPosition = position => Math.floor((position - 1) / 2)
23+
const getChildrenPositions = position => [2 * position + 1, 2 * position + 2]
24+
25+
class KeyPriorityQueue {
26+
// Priority Queue class using Minimum Binary Heap
27+
constructor () {
28+
this._heap = []
29+
this.priorities = new Map()
30+
}
31+
32+
/**
33+
* Checks if the heap is empty
34+
* @returns boolean
35+
*/
36+
isEmpty () {
37+
return this._heap.length === 0
38+
}
39+
40+
/**
41+
* Adds an element to the queue
42+
* @param {*} key
43+
* @param {number} priority
44+
*/
45+
push (key, priority) {
46+
this._heap.push(key)
47+
this.priorities.set(key, priority)
48+
this._shiftUp(this._heap.length - 1)
49+
}
50+
51+
/**
52+
* Removes the element with least priority
53+
* @returns the key of the element with least priority
54+
*/
55+
pop () {
56+
this._swap(0, this._heap.length - 1)
57+
const key = this._heap.pop()
58+
this.priorities.delete(key)
59+
this._shiftDown(0)
60+
return key
61+
}
62+
63+
/**
64+
* Checks whether a given key is present in the queue
65+
* @param {*} key
66+
* @returns boolean
67+
*/
68+
contains (key) {
69+
return this.priorities.has(key)
70+
}
71+
72+
/**
73+
* Updates the priority of the given element.
74+
* Adds the element if it is not in the queue.
75+
* @param {*} key the element to change
76+
* @param {number} priority new priority of the element
77+
*/
78+
update (key, priority) {
79+
const currPos = this._heap.indexOf(key)
80+
// if the key does not exist yet, add it
81+
if (currPos === -1) return this.push(key, priority)
82+
// else update priority
83+
this.priorities.set(key, priority)
84+
const parentPos = getParentPosition(currPos)
85+
const currPriority = this._getPriorityOrInfinite(currPos)
86+
const parentPriority = this._getPriorityOrInfinite(parentPos)
87+
const [child1Pos, child2Pos] = getChildrenPositions(currPos)
88+
const child1Priority = this._getPriorityOrInfinite(child1Pos)
89+
const child2Priority = this._getPriorityOrInfinite(child2Pos)
90+
91+
if (parentPos >= 0 && parentPriority > currPriority) {
92+
this._shiftUp(currPos)
93+
} else if (child1Priority < currPriority || child2Priority < currPriority) {
94+
this._shiftDown(currPos)
95+
}
96+
}
97+
98+
_getPriorityOrInfinite (position) {
99+
// Helper function, returns priority of the node, or Infinite if no node corresponds to this position
100+
if (position >= 0 && position < this._heap.length) return this.priorities.get(this._heap[position])
101+
else return Infinity
102+
}
103+
104+
_shiftUp (position) {
105+
// Helper function to shift up a node to proper position (equivalent to bubbleUp)
106+
let currPos = position
107+
let parentPos = getParentPosition(currPos)
108+
let currPriority = this._getPriorityOrInfinite(currPos)
109+
let parentPriority = this._getPriorityOrInfinite(parentPos)
110+
111+
while (parentPos >= 0 && parentPriority > currPriority) {
112+
this._swap(currPos, parentPos)
113+
currPos = parentPos
114+
parentPos = getParentPosition(currPos)
115+
currPriority = this._getPriorityOrInfinite(currPos)
116+
parentPriority = this._getPriorityOrInfinite(parentPos)
117+
}
118+
}
119+
120+
_shiftDown (position) {
121+
// Helper function to shift down a node to proper position (equivalent to bubbleDown)
122+
let currPos = position
123+
let [child1Pos, child2Pos] = getChildrenPositions(currPos)
124+
let child1Priority = this._getPriorityOrInfinite(child1Pos)
125+
let child2Priority = this._getPriorityOrInfinite(child2Pos)
126+
let currPriority = this._getPriorityOrInfinite(currPos)
127+
128+
if (currPriority === Infinity) {
129+
return
130+
}
131+
132+
while (child1Priority < currPriority || child2Priority < currPriority) {
133+
if (child1Priority < currPriority && child1Priority < child2Priority) {
134+
this._swap(child1Pos, currPos)
135+
currPos = child1Pos
136+
} else {
137+
this._swap(child2Pos, currPos)
138+
currPos = child2Pos
139+
}
140+
[child1Pos, child2Pos] = getChildrenPositions(currPos)
141+
child1Priority = this._getPriorityOrInfinite(child1Pos)
142+
child2Priority = this._getPriorityOrInfinite(child2Pos)
143+
currPriority = this._getPriorityOrInfinite(currPos)
144+
}
145+
}
146+
147+
_swap (position1, position2) {
148+
// Helper function to swap 2 nodes
149+
[this._heap[position1], this._heap[position2]] = [this._heap[position2], this._heap[position1]]
150+
}
151+
}
152+
153+
export { KeyPriorityQueue }
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
import { KeyPriorityQueue } from '../KeyPriorityQueue.js'
2+
3+
describe('Key Priority Queue', () => {
4+
describe('Method isEmpty', () => {
5+
test('Check heap is empty', () => {
6+
const queue = new KeyPriorityQueue()
7+
const res = queue.isEmpty()
8+
expect(res).toEqual(true)
9+
})
10+
11+
test('Check heap is not empty', () => {
12+
const queue = new KeyPriorityQueue()
13+
queue.push(0, 2)
14+
const res = queue.isEmpty()
15+
expect(res).toEqual(false)
16+
})
17+
})
18+
19+
describe('Methods push and pop', () => {
20+
test('Test Case 1', () => {
21+
// create queue
22+
const queue = new KeyPriorityQueue()
23+
queue.push(0, 3)
24+
queue.push(1, 7)
25+
queue.push(2, 9)
26+
queue.push(3, 13)
27+
// create expected queue
28+
const expectedQueue = new KeyPriorityQueue()
29+
expectedQueue.push(1, 7)
30+
expectedQueue.push(3, 13)
31+
expectedQueue.push(2, 9)
32+
// result from popping element from the queue
33+
queue.pop()
34+
expect(queue).toEqual(expectedQueue)
35+
})
36+
37+
test('Test Case 2', () => {
38+
// create queue
39+
const queue = new KeyPriorityQueue()
40+
queue.push(0, 3)
41+
queue.push(1, 9)
42+
queue.push(2, 7)
43+
queue.push(3, 13)
44+
// create expected queue
45+
const expectedQueue = new KeyPriorityQueue()
46+
expectedQueue.push(2, 7)
47+
expectedQueue.push(1, 9)
48+
expectedQueue.push(3, 13)
49+
// result from popping element from the queue
50+
queue.pop()
51+
expect(queue).toEqual(expectedQueue)
52+
})
53+
54+
test('Test Case 3', () => {
55+
// create queue
56+
const queue = new KeyPriorityQueue()
57+
queue.push(0, 3)
58+
queue.push(1, 7)
59+
queue.push(2, 9)
60+
queue.push(3, 12)
61+
queue.push(4, 13)
62+
// create expected queue
63+
const expectedQueue = new KeyPriorityQueue()
64+
expectedQueue.push(1, 7)
65+
expectedQueue.push(3, 12)
66+
expectedQueue.push(2, 9)
67+
expectedQueue.push(4, 13)
68+
// result from popping element from the queue
69+
queue.pop()
70+
expect(queue).toEqual(expectedQueue)
71+
})
72+
})
73+
74+
describe('Method contains', () => {
75+
test('Check heap does not contain element', () => {
76+
const queue = new KeyPriorityQueue()
77+
const res = queue.contains(0)
78+
expect(res).toEqual(false)
79+
})
80+
81+
test('Check heap contains element', () => {
82+
const queue = new KeyPriorityQueue()
83+
queue.push(0, 2)
84+
const res = queue.contains(0)
85+
expect(res).toEqual(true)
86+
})
87+
})
88+
89+
describe('Method update', () => {
90+
test('Update without change in position', () => {
91+
// create queue
92+
const queue = new KeyPriorityQueue()
93+
queue.push(0, 3)
94+
queue.push(1, 5)
95+
queue.push(2, 7)
96+
queue.push(3, 11)
97+
// create expected queue
98+
const expectedQueue = new KeyPriorityQueue()
99+
expectedQueue.push(0, 2)
100+
expectedQueue.push(1, 5)
101+
expectedQueue.push(2, 7)
102+
expectedQueue.push(3, 11)
103+
// result from updating to similar priority
104+
queue.update(0, 2)
105+
expect(queue).toEqual(expectedQueue)
106+
})
107+
108+
test('Update with change in position', () => {
109+
// create queue
110+
const queue = new KeyPriorityQueue()
111+
queue.push(0, 3)
112+
queue.push(1, 5)
113+
queue.push(2, 7)
114+
queue.push(3, 11)
115+
// create expected queue
116+
const expectedQueue = new KeyPriorityQueue()
117+
expectedQueue.push(1, 5)
118+
expectedQueue.push(3, 11)
119+
expectedQueue.push(2, 7)
120+
expectedQueue.push(0, 9)
121+
// result from updating to similar priority
122+
queue.update(0, 9)
123+
expect(queue).toEqual(expectedQueue)
124+
})
125+
})
126+
})

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy