Skip to content

Commit f4ed634

Browse files
JarnikPrim for MST
1 parent 9d9a4e1 commit f4ed634

File tree

3 files changed

+97
-1
lines changed

3 files changed

+97
-1
lines changed
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package algorithms.graph
2+
3+
import datastructures.graph.Graph
4+
import datastructures.basic.PriorityQueue
5+
import scala.collection.mutable
6+
import scala.util.control.Breaks._
7+
8+
/// Jarník-Prim’s Algorithm
9+
/// Computes the Minimum Spanning Tree (MST) of a weighted graph
10+
/// Uses a greedy approach with a priority queue to select the lightest edge
11+
/// Works for connected, undirected graphs
12+
13+
/// Returns a set of edges forming the MST
14+
15+
/// The algorithm runs in O(E log V) time complexity
16+
17+
def jarnikPrim[T](graph: Graph[T]): Set[(T, T, Double)] = {
18+
val vertices = graph.getVertices.toList
19+
if (vertices.isEmpty) return Set.empty // Handle empty graph
20+
21+
val mstEdges = mutable.Set[(T, T, Double)]()
22+
val visited = mutable.Set[T]()
23+
val priorityQueue = PriorityQueue[(T, T, Double)]()(Ordering.by(-_._3)) // Min-Heap
24+
25+
// Start from an arbitrary vertex
26+
val start = vertices.head
27+
visited.add(start)
28+
29+
// Add all edges from the start vertex to the priority queue
30+
for ((neighbor, weight) <- graph.getNeighbors(start)) {
31+
priorityQueue.enqueue((start, neighbor, weight))
32+
}
33+
34+
// Main loop
35+
while (visited.size < vertices.size && !priorityQueue.isEmpty) {
36+
breakable {
37+
val (u: T, v: T, weight: Double) = priorityQueue.dequeue().get
38+
39+
if (visited.contains(v)) break // Ignore edges leading to already visited nodes
40+
41+
// Add the edge to MST
42+
mstEdges.add((u, v, weight))
43+
visited.add(v)
44+
45+
// Add new edges from v to priority queue
46+
for ((neighbor, w) <- graph.getNeighbors(v)) {
47+
if (!visited.contains(neighbor)) {
48+
priorityQueue.enqueue((v, neighbor, w))
49+
}
50+
}
51+
}
52+
}
53+
54+
mstEdges.toSet
55+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package algorithms.graph
2+
3+
import munit.FunSuite
4+
import datastructures.graph.Graph
5+
import algorithms.graph.jarnikPrim
6+
7+
class JarnikPrimTest extends FunSuite {
8+
9+
test("Jarník-Prim should compute MST correctly") {
10+
val graph = new Graph[String](isDirected = false)
11+
graph.addEdge("A", "B", 1.0)
12+
graph.addEdge("A", "C", 3.0)
13+
graph.addEdge("B", "C", 1.0)
14+
graph.addEdge("B", "D", 4.0)
15+
graph.addEdge("C", "D", 1.0)
16+
graph.addEdge("C", "E", 5.0)
17+
graph.addEdge("D", "E", 2.0)
18+
19+
val mst = jarnikPrim(graph)
20+
21+
val expectedMST = Set(
22+
("A", "B", 1.0),
23+
("B", "C", 1.0),
24+
("C", "D", 1.0),
25+
("D", "E", 2.0)
26+
)
27+
28+
assertEquals(mst.size, expectedMST.size)
29+
assertEquals(mst.map(e => (e._1, e._2, e._3)), expectedMST)
30+
}
31+
32+
test("Jarník-Prim should handle a single-node graph") {
33+
val graph = new Graph[String](isDirected = false)
34+
graph.addVertex("A")
35+
36+
val mst = jarnikPrim(graph)
37+
38+
assertEquals(mst, Set.empty)
39+
}
40+
41+
}

src/test/scala/algorithms/graph/JohnsonTest.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ class JohnsonTest extends FunSuite {
3232
}
3333

3434
/* Kind of not working lmao. this is caused by the Sentinel T value used
35-
35+
3636
test("Johnson's Algorithm should detect a negative cycle") {
3737
val graph = new Graph[String](isDirected = true)
3838

0 commit comments

Comments
 (0)