Skip to content

Commit 6a80dc7

Browse files
authored
Added Prim's Algoritm (#227)
1 parent 9030de4 commit 6a80dc7

File tree

1 file changed

+210
-0
lines changed

1 file changed

+210
-0
lines changed

Graphs/PrimMST.js

Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
// Priority Queue Helper functions
2+
function getParentPosition (position) {
3+
// Get the parent node of the current node
4+
return Math.floor((position - 1) / 2)
5+
}
6+
function getChildrenPosition (position) {
7+
// Get the children nodes of the current node
8+
return [2 * position + 1, 2 * position + 2]
9+
}
10+
11+
class PriorityQueue {
12+
// Priority Queue class using Minimum Binary Heap
13+
constructor () {
14+
this._heap = []
15+
this.keys = {}
16+
}
17+
18+
isEmpty () {
19+
// Checking if the heap is empty
20+
return this._heap.length === 0
21+
}
22+
23+
push (key, priority) {
24+
// Adding element to the queue (equivalent to add)
25+
this._heap.push([key, priority])
26+
this.keys[key] = this._heap.length - 1
27+
this._shiftUp(this.keys[key])
28+
}
29+
30+
pop () {
31+
// Removing the element with least priority (equivalent to extractMin)
32+
this._swap(0, this._heap.length - 1)
33+
const [key] = this._heap.pop()
34+
delete this.keys[key]
35+
this._shiftDown(0)
36+
return key
37+
}
38+
39+
contains (key) {
40+
// Check if a given key is present in the queue
41+
return (key in this.keys)
42+
}
43+
44+
update (key, priority) {
45+
// Update the priority of the given element (equivalent to decreaseKey)
46+
const currPos = this.keys[key]
47+
this._heap[currPos][1] = priority
48+
const parentPos = getParentPosition(currPos)
49+
const currPriority = this._heap[currPos][1]
50+
let parentPriority = Infinity
51+
if (parentPos >= 0) {
52+
parentPriority = this._heap[parentPos][1]
53+
}
54+
const [child1Pos, child2Pos] = getChildrenPosition(currPos)
55+
let [child1Priority, child2Priority] = [Infinity, Infinity]
56+
if (child1Pos < this._heap.length) {
57+
child1Priority = this._heap[child1Pos][1]
58+
}
59+
if (child2Pos < this._heap.length) {
60+
child2Priority = this._heap[child2Pos][1]
61+
}
62+
63+
if (parentPos >= 0 && parentPriority > currPriority) {
64+
this._shiftUp(currPos)
65+
} else if (child2Pos < this._heap.length &&
66+
(child1Priority < currPriority || child2Priority < currPriority)) {
67+
this._shiftDown(currPos)
68+
}
69+
}
70+
71+
_shiftUp (position) {
72+
// Helper function to shift up a node to proper position (equivalent to bubbleUp)
73+
let currPos = position
74+
let parentPos = getParentPosition(currPos)
75+
let currPriority = this._heap[currPos][1]
76+
let parentPriority = Infinity
77+
if (parentPos >= 0) {
78+
parentPriority = this._heap[parentPos][1]
79+
}
80+
81+
while (parentPos >= 0 && parentPriority > currPriority) {
82+
this._swap(currPos, parentPos)
83+
currPos = parentPos
84+
parentPos = getParentPosition(currPos)
85+
currPriority = this._heap[currPos][1]
86+
try {
87+
parentPriority = this._heap[parentPos][1]
88+
} catch (error) {
89+
parentPriority = Infinity
90+
}
91+
}
92+
this.keys[this._heap[currPos][0]] = currPos
93+
}
94+
95+
_shiftDown (position) {
96+
// Helper function to shift down a node to proper position (equivalent to bubbleDown)
97+
let currPos = position
98+
let [child1Pos, child2Pos] = getChildrenPosition(currPos)
99+
let [child1Priority, child2Priority] = [Infinity, Infinity]
100+
if (child1Pos < this._heap.length) {
101+
child1Priority = this._heap[child1Pos][1]
102+
}
103+
if (child2Pos < this._heap.length) {
104+
child2Priority = this._heap[child2Pos][1]
105+
}
106+
let currPriority
107+
try {
108+
currPriority = this._heap[currPos][1]
109+
} catch {
110+
return
111+
}
112+
113+
while (child2Pos < this._heap.length &&
114+
(child1Priority < currPriority || child2Priority < currPriority)) {
115+
if (child1Priority < currPriority && child1Priority < child2Priority) {
116+
this._swap(child1Pos, currPos)
117+
currPos = child1Pos
118+
} else {
119+
this._swap(child2Pos, currPos)
120+
currPos = child2Pos
121+
}
122+
[child1Pos, child2Pos] = getChildrenPosition(currPos)
123+
try {
124+
[child1Priority, child2Priority] = [this._heap[child1Pos][1], this._heap[child2Pos][1]]
125+
} catch (error) {
126+
[child1Priority, child2Priority] = [Infinity, Infinity]
127+
}
128+
129+
currPriority = this._heap[currPos][1]
130+
}
131+
this.keys[this._heap[currPos][0]] = currPos
132+
if (child1Pos < this._heap.length && child1Priority < currPriority) {
133+
this._swap(child1Pos, currPos)
134+
this.keys[this._heap[child1Pos][0]] = child1Pos
135+
}
136+
}
137+
138+
_swap (position1, position2) {
139+
// Helper function to swap 2 nodes
140+
[this._heap[position1], this._heap[position2]] = [this._heap[position2], this._heap[position1]]
141+
this.keys[this._heap[position1][0]] = position1
142+
this.keys[this._heap[position2][0]] = position2
143+
}
144+
}
145+
146+
class GraphWeightedUndirectedAdjacencyList {
147+
// Weighted Undirected Graph class
148+
constructor () {
149+
this.connections = {}
150+
}
151+
152+
addNode (node) {
153+
// Function to add a node to the graph (connection represented by set)
154+
this.connections[node] = {}
155+
}
156+
157+
addEdge (node1, node2, weight) {
158+
// Function to add an edge (adds the node too if they are not present in the graph)
159+
if (!(node1 in this.connections)) { this.addNode(node1) }
160+
if (!(node2 in this.connections)) { this.addNode(node2) }
161+
this.connections[node1][node2] = weight
162+
this.connections[node2][node1] = weight
163+
}
164+
165+
PrimMST (start) {
166+
// Kruskal's Algorithm to generate a Minimum Spanning Tree (MST) of a graph
167+
// Details: https://en.wikipedia.org/wiki/Prim%27s_algorithm
168+
const distance = {}
169+
const parent = {}
170+
const priorityQueue = new PriorityQueue()
171+
// Initialization
172+
for (const node in this.connections) {
173+
distance[node] = (node === start.toString() ? 0 : Infinity)
174+
parent[node] = null
175+
priorityQueue.push(node, distance[node])
176+
}
177+
// Updating 'distance' object
178+
while (!priorityQueue.isEmpty()) {
179+
const node = priorityQueue.pop()
180+
Object.keys(this.connections[node]).forEach(neighbour => {
181+
if (priorityQueue.contains(neighbour) && distance[node] + this.connections[node][neighbour] < distance[neighbour]) {
182+
distance[neighbour] = distance[node] + this.connections[node][neighbour]
183+
parent[neighbour] = node
184+
priorityQueue.update(neighbour, distance[neighbour])
185+
}
186+
})
187+
}
188+
189+
// MST Generation from the 'parent' object
190+
const graph = new GraphWeightedUndirectedAdjacencyList()
191+
Object.keys(parent).forEach(node => {
192+
if (node && parent[node]) {
193+
graph.addEdge(node, parent[node], this.connections[node][parent[node]])
194+
}
195+
})
196+
return graph
197+
}
198+
}
199+
200+
function main () {
201+
const graph = new GraphWeightedUndirectedAdjacencyList()
202+
graph.addEdge(1, 2, 1)
203+
graph.addEdge(2, 3, 2)
204+
graph.addEdge(3, 4, 1)
205+
graph.addEdge(3, 5, 100) // Removed in MST
206+
graph.addEdge(4, 5, 5)
207+
console.log(graph.PrimMST(1))
208+
}
209+
210+
main()

0 commit comments

Comments
 (0)