Skip to content

Commit b72dfb3

Browse files
implemented DAGSSSP based on TopoSort
1 parent 4e8728e commit b72dfb3

File tree

4 files changed

+131
-25
lines changed

4 files changed

+131
-25
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package algorithms.graph
2+
3+
import datastructures.graph.Graph
4+
import algorithms.graph.topologicalSort
5+
6+
/// Single Source Shortest Paths (SSSP) in a Directed Acyclic Graph (DAG)
7+
/// Uses topological sorting and edge relaxation for shortest path calculation
8+
/// Returns a map of distances from the start vertex to all other vertices
9+
10+
/// The algorithm runs in O(V + E) time complexity
11+
def DAGSingleSourceShortestPaths[T](graph: Graph[T], s: T): (Map[T, Double], Map[T, T]) = {
12+
val maybeSortedList = topologicalSort(graph)
13+
val sortedList = maybeSortedList.getOrElse(List.empty[T])
14+
15+
var dist: Map[T, Double] = sortedList.map(v => v -> Double.PositiveInfinity).toMap
16+
var pred: Map[T, T] = Map()
17+
18+
dist = dist.updated(s, 0)
19+
20+
for (v <- sortedList) {
21+
for ((u, weight) <- graph.getNeighbors(v)) {
22+
if (dist(v) != Double.PositiveInfinity && dist(u) > dist(v) + weight) {
23+
dist = dist.updated(u, dist(v) + weight)
24+
pred = pred.updated(u, v)
25+
}
26+
}
27+
}
28+
29+
(dist, pred)
30+
}

src/main/scala/algorithms/graph/TopoSort.scala

+27-25
Original file line numberDiff line numberDiff line change
@@ -3,40 +3,42 @@ package algorithms.graph
33
import datastructures.graph._
44
import scala.collection.mutable
55

6-
//Kahns Algorithm
7-
def topologicalSort[T](graph : Graph[T]) : Option[List[T]] = {
6+
/// Topological Sort of a Directed Acyclic Graph (DAG)
7+
/// Implements Kahn’s Algorithm (BFS-based)
8+
/// Returns an ordering of vertices such that for every directed edge (u, v), u appears before v.
9+
/// Detects cycles: If a cycle exists, returns None.
810

11+
/// Topological Sort runs in O(V + E)
12+
def topologicalSort[T](graph: Graph[T]): Option[List[T]] = {
913
val inDegree = mutable.Map[T, Int]().withDefaultValue(0)
1014

11-
for ((src, neighbors) <- graph.adjacencyList) {
12-
for ((dest, _) <- neighbors) {
13-
inDegree.update(dest, inDegree(dest) + 1)
14-
}
15+
for ((src, neighbors) <- graph.adjacencyList) {
16+
for ((dest, _) <- neighbors) {
17+
inDegree.update(dest, inDegree(dest) + 1)
1518
}
19+
}
1620

17-
// Initialize queue with nodes having in-degree 0
18-
val queue = mutable.Queue[T]()
19-
for (vertex <- graph.adjacencyList.keys if inDegree(vertex) == 0) {
20-
queue.enqueue(vertex)
21-
}
21+
// Initialize queue with nodes having in-degree 0
22+
val queue = mutable.Queue[T]()
23+
for (vertex <- graph.adjacencyList.keys if inDegree(vertex) == 0) {
24+
queue.enqueue(vertex)
25+
}
2226

23-
val sortedList = mutable.ListBuffer[T]()
27+
val sortedList = mutable.ListBuffer[T]()
2428

25-
// Process queue
26-
while (queue.nonEmpty) {
27-
val vertex = queue.dequeue()
28-
sortedList.append(vertex)
29+
// Process queue
30+
while (queue.nonEmpty) {
31+
val vertex = queue.dequeue()
32+
sortedList.append(vertex)
2933

30-
for ((neighbor, _) <- graph.adjacencyList(vertex)) {
31-
inDegree.update(neighbor, inDegree(neighbor) - 1)
32-
if (inDegree(neighbor) == 0) {
33-
queue.enqueue(neighbor)
34-
}
34+
for ((neighbor, _) <- graph.adjacencyList(vertex)) {
35+
inDegree.update(neighbor, inDegree(neighbor) - 1)
36+
if (inDegree(neighbor) == 0) {
37+
queue.enqueue(neighbor)
3538
}
3639
}
40+
}
3741

38-
// Cycle check
39-
if (sortedList.size == graph.adjacencyList.size) Some(sortedList.toList) else None
42+
// Cycle check
43+
if (sortedList.size == graph.adjacencyList.size) Some(sortedList.toList) else None
4044
}
41-
42-
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package algorithms.graph
2+
3+
import datastructures.graph._
4+
import munit.FunSuite
5+
6+
class DAGSingleSourceShortestPathsTest extends FunSuite {
7+
8+
test("DAG SSSP should return correct shortest paths and predecessors") {
9+
val graph = new Graph[Char](true) // Directed Graph
10+
graph.addEdge('A', 'B', 1)
11+
graph.addEdge('A', 'C', 4)
12+
graph.addEdge('B', 'C', 2)
13+
graph.addEdge('B', 'D', 5)
14+
graph.addEdge('C', 'D', 1)
15+
16+
val (distances, predecessors) = DAGSingleSourceShortestPaths(graph, 'A')
17+
18+
// Expected distances from 'A'
19+
val expectedDistances = Map('A' -> 0.0, 'B' -> 1.0, 'C' -> 3.0, 'D' -> 4.0)
20+
assertEquals(distances, expectedDistances)
21+
22+
// Expected predecessors
23+
val expectedPredecessors = Map('B' -> 'A', 'C' -> 'B', 'D' -> 'C')
24+
assertEquals(predecessors, expectedPredecessors)
25+
}
26+
27+
test("DAG SSSP should work with a single-node graph") {
28+
val graph = new Graph[Char](true)
29+
graph.addVertex('A')
30+
31+
val (distances, predecessors) = DAGSingleSourceShortestPaths(graph, 'A')
32+
33+
assertEquals(distances, Map('A' -> 0.0))
34+
}
35+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package algorithms.graph
2+
3+
import datastructures.graph._
4+
import munit.FunSuite
5+
6+
class TopologicalSortTest extends FunSuite {
7+
8+
test("Topological sort returns valid order for DAG") {
9+
val graph = new Graph[Char](true) // Directed Graph
10+
graph.addEdge('A', 'B')
11+
graph.addEdge('A', 'C')
12+
graph.addEdge('B', 'D')
13+
graph.addEdge('C', 'D')
14+
graph.addEdge('D', 'E')
15+
16+
val result = topologicalSort(graph)
17+
18+
assert(result.isDefined, "Topological sort should return Some(List) for a DAG")
19+
20+
val sortedOrder = result.get
21+
val indexMap = sortedOrder.zipWithIndex.toMap
22+
23+
// Ensure each edge (u, v) respects u before v
24+
for (Edge(src, dest, _) <- graph.getEdges) {
25+
assert(indexMap(src) < indexMap(dest), s"Edge $src -> $dest violated topological order")
26+
}
27+
}
28+
29+
test("Topological sort returns None for cyclic graph") {
30+
val cyclicGraph = new Graph[Char](true) // Directed Graph
31+
cyclicGraph.addEdge('A', 'B')
32+
cyclicGraph.addEdge('B', 'C')
33+
cyclicGraph.addEdge('C', 'A') // Creates a cycle
34+
35+
val result = topologicalSort(cyclicGraph)
36+
37+
assert(result.isEmpty, "Topological sort should return None for cyclic graphs")
38+
}
39+
}

0 commit comments

Comments
 (0)