Strongly Connected Components using Kosaraju's Algorithm
Problem and examples
Given a directed graph with V vertices (numbered from 0 to V - 1) and E directed edges, your task is to find the number of Strongly Connected Components (SCCs).
A strongly connected component is a maximal group of vertices such that every vertex is reachable from every other vertex in the group via directed paths.
| Graph (Adjacency List) | Vertices | Edges | Output | Description |
|---|---|---|---|---|
| { 0: [1], 1: [2], 2: [0], 3: [4] } | 5 | [(0→1), (1→2), (2→0), (3→4)] | 2 | Two SCCs: {0,1,2} and {3,4} |
| { 0: [1], 1: [2], 2: [3], 3: [] } | 4 | [(0→1), (1→2), (2→3)] | 4 | No cycles, each node is its own SCC |
| {} | 0 | [] | 0 | Empty graph has no components |
| { 0: [0] } | 1 | [(0→0)] | 1 | Self loop is a valid SCC |
| { 0: [1], 1: [2], 2: [0], 3: [2,4], 4: [3] } | 5 | [(0→1), (1→2), (2→0), (3→2), (3→4), (4→3)] | 2 | {0,1,2} and {3,4} are two SCCs |
How it works
Understanding the Problem
In a directed graph, a Strongly Connected Component (SCC) is a group of vertices where every vertex is reachable from every other vertex in that group. In simpler terms, if you can start at any node in the group and reach all the others through the directed edges—and also get back to the starting node—then those nodes form an SCC.
Our goal is to identify all such strongly connected components in a given directed graph. We'll solve this using Kosaraju's Algorithm, which is efficient and beginner-friendly once understood step-by-step.
Step-by-Step Solution with Example
Step 1: Do a DFS on the original graph and record finish times
Start by doing a standard DFS traversal of the graph. As we finish exploring each node (i.e., all its neighbors are visited), we push it onto a stack. This stack captures the nodes in the order of their finishing times—the last finished node ends up on top.
Why? This helps us process nodes in the right order during the second DFS on the transposed graph.
Step 2: Transpose the graph (reverse all edges)
Next, we reverse all the directed edges in the graph. That is, if there was an edge from A → B, in the transposed graph it becomes B → A.
This reversal helps us discover which nodes can be reached in reverse direction, a key aspect in identifying SCCs.
Step 3: Do DFS on the transposed graph using the stack order
Now, we pop nodes one by one from the stack and perform DFS on the transposed graph. Each time we start a new DFS from an unvisited node, we find one SCC—all the nodes reached in this DFS form a strongly connected component.
Since the stack ensures we process nodes in decreasing order of finish times, we are guaranteed that whenever we start a new DFS, we start from a root node of some SCC.
Example: Step-by-step on a sample graph
Graph:
Vertices = {0, 1, 2, 3, 4}
Edges = {
0 → 2,
2 → 1,
1 → 0,
0 → 3,
3 → 4
}
Step 1: DFS Finish Order (pushed to stack): [4, 3, 0, 1, 2]
Step 2: Transpose Graph:
2 → 0
1 → 2
0 → 1
3 → 0
4 → 3
Step 3: DFS on Transposed Graph in Stack Order:
- Start DFS from 2 → reaches 2, 1, 0 → SCC1 = [2,1,0]
- Next node in stack is 1 → already visited
- Next is 0 → already visited
- Next is 3 → reaches 3 → SCC2 = [3]
- Next is 4 → reaches 4 → SCC3 = [4]
Final SCCs: [2,1,0], [3], [4]
Edge Cases
- Empty Graph: No vertices means no SCCs to find.
- Single Node: A single node with or without a self-loop is an SCC by itself.
- Disconnected Nodes: Nodes with no outgoing or incoming edges form their own SCCs.
- Multiple Cycles: Kosaraju's algorithm naturally groups cycles correctly into separate SCCs.
Finally
Kosaraju’s Algorithm is a powerful and intuitive method to identify strongly connected components. Its beauty lies in the simplicity of using two DFS traversals—first to record finish order, and second to explore reversed connectivity.
It runs in linear time O(V + E), making it suitable even for large graphs.
As you work through the steps, always remember that SCCs are about mutual reachability, and DFS with finish order is the key to unlocking that structure.
- Initialize a visited array and an empty stack.
- For each unvisited vertex, perform DFS and push the vertex onto the stack after visiting all its descendants.
- Reverse all edges of the graph (create a transpose).
- Reset the visited array.
- While the stack is not empty:
- Pop a vertex from the stack.
- If it hasn't been visited, perform DFS from that node in the transposed graph.
- Each such DFS traversal gives one strongly connected component.
Implementation and efficiency
C implementation
#include <stdio.h>
#include <stdlib.h>
#define MAX_VERTICES 100
// Graph adjacency list node
typedef struct Node {
int vertex;
struct Node* next;
} Node;
// Graph structure
typedef struct Graph {
int numVertices;
Node* adjLists[MAX_VERTICES];
int visited[MAX_VERTICES];
} Graph;
Node* createNode(int v) {
Node* newNode = (Node*)malloc(sizeof(Node));
newNode->vertex = v;
newNode->next = NULL;
return newNode;
}
Graph* createGraph(int vertices) {
Graph* graph = (Graph*)malloc(sizeof(Graph));
graph->numVertices = vertices;
for (int i = 0; i < vertices; i++) {
graph->adjLists[i] = NULL;
graph->visited[i] = 0;
}
return graph;
}
void addEdge(Graph* graph, int src, int dest) {
Node* newNode = createNode(dest);
newNode->next = graph->adjLists[src];
graph->adjLists[src] = newNode;
}
void dfs(Graph* graph, int vertex, int* stack, int* stackIndex) {
graph->visited[vertex] = 1;
Node* temp = graph->adjLists[vertex];
while (temp) {
int adjVertex = temp->vertex;
if (!graph->visited[adjVertex]) {
dfs(graph, adjVertex, stack, stackIndex);
}
temp = temp->next;
}
stack[(*stackIndex)++] = vertex;
}
void dfsTranspose(Graph* graph, int vertex, int* visited) {
visited[vertex] = 1;
Node* temp = graph->adjLists[vertex];
while (temp) {
int adjVertex = temp->vertex;
if (!visited[adjVertex]) {
dfsTranspose(graph, adjVertex, visited);
}
temp = temp->next;
}
}
Graph* getTranspose(Graph* graph) {
Graph* gT = createGraph(graph->numVertices);
for (int v = 0; v < graph->numVertices; v++) {
Node* temp = graph->adjLists[v];
while (temp) {
addEdge(gT, temp->vertex, v);
temp = temp->next;
}
}
return gT;
}
int kosarajuSCC(Graph* graph) {
int stack[MAX_VERTICES];
int stackIndex = 0;
for (int i = 0; i < graph->numVertices; i++) {
if (!graph->visited[i]) {
dfs(graph, i, stack, &stackIndex);
}
}
Graph* transposeGraph = getTranspose(graph);
int visited[MAX_VERTICES] = {0};
int count = 0;
for (int i = stackIndex - 1; i >= 0; i--) {
int v = stack[i];
if (!visited[v]) {
dfsTranspose(transposeGraph, v, visited);
count++;
}
}
return count;
}
int main() {
int V = 5;
Graph* graph = createGraph(V);
addEdge(graph, 0, 1);
addEdge(graph, 1, 2);
addEdge(graph, 2, 0);
addEdge(graph, 3, 4);
int sccCount = kosarajuSCC(graph);
printf("Number of Strongly Connected Components: %d\n", sccCount);
return 0;
}C++ implementation
#include <iostream>
#include <vector>
#include <stack>
using namespace std;
class Graph {
int V;
vector<vector<int>> adj;
public:
Graph(int V) {
this->V = V;
adj.resize(V);
}
void addEdge(int u, int v) {
adj[u].push_back(v);
}
void dfs(int v, vector<bool>& visited, stack<int>& Stack) {
visited[v] = true;
for (int u : adj[v]) {
if (!visited[u]) dfs(u, visited, Stack);
}
Stack.push(v);
}
void dfsTranspose(int v, vector<bool>& visited, vector<vector<int>>& transposeAdj) {
visited[v] = true;
for (int u : transposeAdj[v]) {
if (!visited[u]) dfsTranspose(u, visited, transposeAdj);
}
}
int kosarajuSCC() {
stack<int> Stack;
vector<bool> visited(V, false);
for (int i = 0; i < V; i++) {
if (!visited[i]) dfs(i, visited, Stack);
}
vector<vector<int>> transposeAdj(V);
for (int i = 0; i < V; i++) {
for (int u : adj[i]) {
transposeAdj[u].push_back(i);
}
}
fill(visited.begin(), visited.end(), false);
int count = 0;
while (!Stack.empty()) {
int v = Stack.top();
Stack.pop();
if (!visited[v]) {
dfsTranspose(v, visited, transposeAdj);
count++;
}
}
return count;
}
};
int main() {
int V = 5;
Graph graph(V);
graph.addEdge(0, 1);
graph.addEdge(1, 2);
graph.addEdge(2, 0);
graph.addEdge(3, 4);
cout << "Number of Strongly Connected Components: " << graph.kosarajuSCC() << endl;
return 0;
}Python implementation
from collections import defaultdict
class Graph:
def __init__(self, vertices):
self.V = vertices
self.graph = defaultdict(list)
def add_edge(self, u, v):
self.graph[u].append(v)
def dfs(self, v, visited, stack):
visited[v] = True
for i in self.graph[v]:
if not visited[i]:
self.dfs(i, visited, stack)
stack.append(v)
def transpose(self):
g = Graph(self.V)
for u in self.graph:
for v in self.graph[u]:
g.add_edge(v, u)
return g
def dfs_transpose(self, v, visited):
visited[v] = True
for i in self.graph[v]:
if not visited[i]:
self.dfs_transpose(i, visited)
def kosaraju_scc(self):
visited = [False] * self.V
stack = []
for i in range(self.V):
if not visited[i]:
self.dfs(i, visited, stack)
gr = self.transpose()
visited = [False] * self.V
count = 0
while stack:
v = stack.pop()
if not visited[v]:
gr.dfs_transpose(v, visited)
count += 1
return count
if __name__ == "__main__":
g = Graph(5)
g.add_edge(0, 1)
g.add_edge(1, 2)
g.add_edge(2, 0)
g.add_edge(3, 4)
print("Number of Strongly Connected Components:", g.kosaraju_scc())Java implementation
import java.util.*;
public class KosarajuSCC {
private int V;
private List<List<Integer>> adj;
public KosarajuSCC(int V) {
this.V = V;
adj = new ArrayList<>();
for (int i = 0; i < V; i++) {
adj.add(new ArrayList<>());
}
}
public void addEdge(int u, int v) {
adj.get(u).add(v);
}
private void dfs(int v, boolean[] visited, Stack<Integer> stack) {
visited[v] = true;
for (int u : adj.get(v)) {
if (!visited[u]) dfs(u, visited, stack);
}
stack.push(v);
}
private void dfsTranspose(int v, boolean[] visited, List<List<Integer>> transposeAdj) {
visited[v] = true;
for (int u : transposeAdj.get(v)) {
if (!visited[u]) dfsTranspose(u, visited, transposeAdj);
}
}
private List<List<Integer>> getTranspose() {
List<List<Integer>> transposeAdj = new ArrayList<>();
for (int i = 0; i < V; i++) {
transposeAdj.add(new ArrayList<>());
}
for (int v = 0; v < V; v++) {
for (int u : adj.get(v)) {
transposeAdj.get(u).add(v);
}
}
return transposeAdj;
}
public int kosarajuSCC() {
boolean[] visited = new boolean[V];
Stack<Integer> stack = new Stack<>();
for (int i = 0; i < V; i++) {
if (!visited[i]) dfs(i, visited, stack);
}
List<List<Integer>> transposeAdj = getTranspose();
Arrays.fill(visited, false);
int count = 0;
while (!stack.isEmpty()) {
int v = stack.pop();
if (!visited[v]) {
dfsTranspose(v, visited, transposeAdj);
count++;
}
}
return count;
}
public static void main(String[] args) {
KosarajuSCC graph = new KosarajuSCC(5);
graph.addEdge(0, 1);
graph.addEdge(1, 2);
graph.addEdge(2, 0);
graph.addEdge(3, 4);
System.out.println("Number of Strongly Connected Components: " + graph.kosarajuSCC());
}
}JavaScript implementation
function kosarajuSCC(V, adj) {
const visited = new Array(V).fill(false);
const stack = [];
function dfs(v) {
visited[v] = true;
for (let nei of adj[v] || []) {
if (!visited[nei]) dfs(nei);
}
stack.push(v);
}
for (let i = 0; i < V; i++) {
if (!visited[i]) dfs(i);
}
const transpose = Array.from({ length: V }, () => []);
for (let u = 0; u < V; u++) {
for (let v of adj[u] || []) {
transpose[v].push(u);
}
}
visited.fill(false);
let count = 0;
function reverseDFS(v) {
visited[v] = true;
for (let nei of transpose[v]) {
if (!visited[nei]) reverseDFS(nei);
}
}
while (stack.length > 0) {
const node = stack.pop();
if (!visited[node]) {
reverseDFS(node);
count++;
}
}
return count;
}
// Example usage
const graph = {
0: [1],
1: [2],
2: [0],
3: [4]
};
console.log("Number of Strongly Connected Components:", kosarajuSCC(5, graph));Go implementation
package main
import "fmt"
func dfs(v int, visited []bool, adj [][]int, stack *[]int) {
visited[v] = true
for _, u := range adj[v] {
if !visited[u] {
dfs(u, visited, adj, stack)
}
}
*stack = append(*stack, v)
}
func dfsTranspose(v int, visited []bool, transpose [][]int) {
visited[v] = true
for _, u := range transpose[v] {
if !visited[u] {
dfsTranspose(u, visited, transpose)
}
}
}
func getTranspose(V int, adj [][]int) [][]int {
transpose := make([][]int, V)
for i := 0; i < V; i++ {
transpose[i] = []int{}
}
for u := 0; u < V; u++ {
for _, v := range adj[u] {
transpose[v] = append(transpose[v], u)
}
}
return transpose
}
func kosarajuSCC(V int, adj [][]int) int {
visited := make([]bool, V)
var stack []int
for i := 0; i < V; i++ {
if !visited[i] {
dfs(i, visited, adj, &stack)
}
}
transpose := getTranspose(V, adj)
for i := range visited {
visited[i] = false
}
count := 0
for i := len(stack) - 1; i >= 0; i-- {
v := stack[i]
if !visited[v] {
dfsTranspose(v, visited, transpose)
count++
}
}
return count
}
func main() {
V := 5
adj := make([][]int, V)
adj[0] = []int{1}
adj[1] = []int{2}
adj[2] = []int{0}
adj[3] = []int{4}
sccCount := kosarajuSCC(V, adj)
fmt.Printf("Number of Strongly Connected Components: %d\n", sccCount)
}Rust implementation
use std::collections::VecDeque;
fn dfs(v: usize, visited: &mut Vec<bool>, adj: &Vec<Vec<usize>>, stack: &mut Vec<usize>) {
visited[v] = true;
for &u in &adj[v] {
if !visited[u] {
dfs(u, visited, adj, stack);
}
}
stack.push(v);
}
fn dfs_transpose(v: usize, visited: &mut Vec<bool>, transpose: &Vec<Vec<usize>>) {
visited[v] = true;
for &u in &transpose[v] {
if !visited[u] {
dfs_transpose(u, visited, transpose);
}
}
}
fn get_transpose(V: usize, adj: &Vec<Vec<usize>>) -> Vec<Vec<usize>> {
let mut transpose = vec![vec![]; V];
for u in 0..V {
for &v in &adj[u] {
transpose[v].push(u);
}
}
transpose
}
fn kosaraju_scc(V: usize, adj: Vec<Vec<usize>>) -> usize {
let mut visited = vec![false; V];
let mut stack = Vec::new();
for i in 0..V {
if !visited[i] {
dfs(i, &mut visited, &adj, &mut stack);
}
}
let transpose = get_transpose(V, &adj);
visited.fill(false);
let mut count = 0;
while let Some(v) = stack.pop() {
if !visited[v] {
dfs_transpose(v, &mut visited, &transpose);
count += 1;
}
}
count
}
fn main() {
let V = 5;
let adj = vec![
vec![1],
vec![2],
vec![0],
vec![4],
vec![],
];
let scc_count = kosaraju_scc(V, adj);
println!("Number of Strongly Connected Components: {}", scc_count);
}Kotlin implementation
fun main() {
val V = 5
val adj = Array(V) { mutableListOf<Int>() }
adj[0].add(1)
adj[1].add(2)
adj[2].add(0)
adj[3].add(4)
val visited = BooleanArray(V) { false }
val stack = mutableListOf<Int>()
fun dfs(v: Int) {
visited[v] = true
for (u in adj[v]) {
if (!visited[u]) dfs(u)
}
stack.add(v)
}
for (i in 0 until V) {
if (!visited[i]) dfs(i)
}
val transpose = Array(V) { mutableListOf<Int>() }
for (u in 0 until V) {
for (v in adj[u]) {
transpose[v].add(u)
}
}
visited.fill(false)
fun dfsTranspose(v: Int) {
visited[v] = true
for (u in transpose[v]) {
if (!visited[u]) dfsTranspose(u)
}
}
var count = 0
while (stack.isNotEmpty()) {
val v = stack.removeAt(stack.size - 1)
if (!visited[v]) {
dfsTranspose(v)
count++
}
}
println("Number of Strongly Connected Components: $count")
}Swift implementation
import Foundation
func dfs(_ v: Int, _ visited: inout [Bool], _ adj: [[Int]], _ stack: inout [Int]) {
visited[v] = true
for u in adj[v] {
if !visited[u] {
dfs(u, &visited, adj, &stack)
}
}
stack.append(v)
}
func dfsTranspose(_ v: Int, _ visited: inout [Bool], _ transpose: [[Int]]) {
visited[v] = true
for u in transpose[v] {
if !visited[u] {
dfsTranspose(u, &visited, transpose)
}
}
}
func getTranspose(_ V: Int, _ adj: [[Int]]) -> [[Int]] {
var transpose = Array(repeating: [Int](), count: V)
for u in 0 ..< V {
for v in adj[u] {
transpose[v].append(u)
}
}
return transpose
}
func kosarajuSCC(_ V: Int, _ adj: [[Int]]) -> Int {
var visited = Array(repeating: false, count: V)
var stack = [Int]()
for i in 0 ..< V {
if !visited[i] {
dfs(i, &visited, adj, &stack)
}
}
let transpose = getTranspose(V, adj)
visited = Array(repeating: false, count: V)
var count = 0
while !stack.isEmpty {
let v = stack.removeLast()
if !visited[v] {
dfsTranspose(v, &visited, transpose)
count += 1
}
}
return count
}
let V = 5
let adj = [
[1],
[2],
[0],
[4],
[]
]
let sccCount = kosarajuSCC(V, adj)
print("Number of Strongly Connected Components: \(sccCount)")TypeScript implementation
function kosarajuSCC(V: number, adj: Record<number, number[]>): number {
const visited: boolean[] = new Array(V).fill(false);
const stack: number[] = [];
function dfs(v: number): void {
visited[v] = true;
for (const nei of adj[v] || []) {
if (!visited[nei]) dfs(nei);
}
stack.push(v);
}
for (let i = 0; i < V; i++) {
if (!visited[i]) dfs(i);
}
const transpose: number[][] = Array.from({ length: V }, () => []);
for (let u = 0; u < V; u++) {
for (const v of adj[u] || []) {
transpose[v].push(u);
}
}
visited.fill(false);
let count = 0;
function reverseDFS(v: number): void {
visited[v] = true;
for (const nei of transpose[v]) {
if (!visited[nei]) reverseDFS(nei);
}
}
while (stack.length > 0) {
const node = stack.pop()!;
if (!visited[node]) {
reverseDFS(node);
count++;
}
}
return count;
}
const graph: Record<number, number[]> = {
0: [1],
1: [2],
2: [0],
3: [4]
};
console.log("Number of Strongly Connected Components:", kosarajuSCC(5, graph));