Kosaraju’s Algorithm in Kotlin | Graph

Kosaraju’s algorithm is a linear time algorithm for finding all strongly connected components (SCCs) in a directed graph. The algorithm was developed by S. Rao Kosaraju in 1978, and it is widely used due to its simplicity and efficiency.

The algorithm works by performing two depth-first searches (DFS) on the graph. First, it performs a DFS on the graph to compute the finishing times of all vertices. Then, it reverses the graph and performs another DFS on the reversed graph, starting from the vertex with the highest finishing time. This second DFS identifies all vertices in the same SCC as the starting vertex.

The algorithm uses two main data structures: a stack to keep track of vertices during the DFS traversal, and a boolean array to keep track of which vertices have already been visited. By performing the two DFS traversals, the algorithm efficiently finds all strongly connected components in the graph.

Overall, Kosaraju’s algorithm is an efficient and simple algorithm for finding strongly connected components in a directed graph. Its linear time complexity makes it well-suited for large graphs.

Kotlin
import java.util.Stack

class KosarajuSCC(private val graph: List<List<Int>>) {

    fun findSCCs(): List<List<Int>> {
        val n = graph.size
        val visited = BooleanArray(n) { false }
        val stack = Stack<Int>()
        val sccs = mutableListOf<List<Int>>()

        // First DFS traversal to compute finishing times
        for (v in 0 until n) {
            if (!visited[v]) {
                dfs1(v, visited, stack)
            }
        }

        // Reverse the graph
        val reversedGraph = Array(n) { mutableListOf<Int>() }
        for (v in 0 until n) {
            for (w in graph[v]) {
                reversedGraph[w].add(v)
            }
        }

        // Second DFS traversal to find SCCs
        visited.fill(false)
        while (!stack.isEmpty()) {
            val v = stack.pop()
            if (!visited[v]) {
                val scc = mutableListOf<Int>()
                dfs2(v, visited, reversedGraph, scc)
                sccs.add(scc)
            }
        }

        return sccs
    }

    private fun dfs1(v: Int, visited: BooleanArray, stack: Stack<Int>) {
        visited[v] = true
        for (w in graph[v]) {
            if (!visited[w]) {
                dfs1(w, visited, stack)
            }
        }
        stack.push(v)
    }

    private fun dfs2(v: Int, visited: BooleanArray, graph: Array<MutableList<Int>>, scc: MutableList<Int>) {
        visited[v] = true
        scc.add(v)
        for (w in graph[v]) {
            if (!visited[w]) {
                dfs2(w, visited, graph, scc)
            }
        }
    }
}

The KosarajuSCC class takes a directed graph represented as a list of adjacency lists, where each element in the list represents a vertex and its adjacent vertices. The findSCCs() function returns a list of lists, where each inner list represents a strongly connected component in the graph.

The algorithm uses two DFS traversals. The first DFS traversal computes the finishing times of each vertex and pushes them onto a stack. The second DFS traversal starts at the vertex with the highest finishing time and identifies all vertices in the same SCC as that vertex.

The dfs1() function performs the first DFS traversal, while the dfs2() function performs the second DFS traversal. The stack is used to keep track of the vertices in the order of their finishing times. The reversedGraph is a reversed version of the original graph, which is used in the second DFS traversal. The visited array keeps track of which vertices have already been visited during the DFS traversal.

Overall, this Kotlin implementation of Kosaraju’s algorithm is an efficient way to find strongly connected components in a directed graph.

Kotlin
import java.util.Stack

class KosarajuSCC(private val graph: List<List<Int>>) {

    fun findSCCs(): List<List<Int>> {
        val n = graph.size
        val visited = BooleanArray(n) { false }
        val stack = Stack<Int>()
        val sccs = mutableListOf<List<Int>>()

        // First DFS traversal to compute finishing times
        for (v in 0 until n) {
            if (!visited[v]) {
                dfs1(v, visited, stack)
            }
        }

        // Reverse the graph
        val reversedGraph = Array(n) { mutableListOf<Int>() }
        for (v in 0 until n) {
            for (w in graph[v]) {
                reversedGraph[w].add(v)
            }
        }

        // Second DFS traversal to find SCCs
        visited.fill(false)
        while (!stack.isEmpty()) {
            val v = stack.pop()
            if (!visited[v]) {
                val scc = mutableListOf<Int>()
                dfs2(v, visited, reversedGraph, scc)
                sccs.add(scc)
            }
        }

        return sccs
    }

    private fun dfs1(v: Int, visited: BooleanArray, stack: Stack<Int>) {
        visited[v] = true
        for (w in graph[v]) {
            if (!visited[w]) {
                dfs1(w, visited, stack)
            }
        }
        stack.push(v)
    }

    private fun dfs2(v: Int, visited: BooleanArray, graph: Array<MutableList<Int>>, scc: MutableList<Int>) {
        visited[v] = true
        scc.add(v)
        for (w in graph[v]) {
            if (!visited[w]) {
                dfs2(w, visited, graph, scc)
            }
        }
    }
}
fun main() {
    val graph = listOf(
        listOf(1, 2),   // vertex 0
        listOf(2),      // vertex 1
        listOf(0, 3),   // vertex 2
        listOf(4),      // vertex 3
        listOf(3)       // vertex 4
    )

    val kosaraju = KosarajuSCC(graph)
    val sccs = kosaraju.findSCCs()

    println("Strongly Connected Components:")
    for (scc in sccs) {
        println(scc.joinToString())
    }
}

In this example, we create a directed graph represented as a list of adjacency lists, where each element in the list represents a vertex and its adjacent vertices. We then create a KosarajuSCC object with the graph and use its findSCCs() function to find the strongly connected components in the graph. Finally, we print out the strongly connected components to the console.

Output :

Strongly Connected Components: 0, 2, 1 & 3, 4

Leave a Comment