Comparison between Tarjan’s and Kosaraju’s Algorithm
Tarjan’s Algorithm: The Tarjan’s Algorithm is an efficient graph algorithm that is used to find the Strongly Connected Component(SCC) in a directed graph by using only one DFS traversal in linear time complexity.
Working:
- Perform a DFS traversal over the nodes so that the sub-trees of the Strongly Connected Components are removed when they are encountered.
- Then two values are assigned:
- The first value is the counter value when the node is explored for the first time.
- Second value stores the lowest node value reachable from the initial node which is not part of another SCC.
- When the nodes are explored, they are pushed into a stack.
- If there are any unexplored children of a node are left, they are explored and the assigned value is respectively updated.
Below is the program to find the SCC of the given graph using Tarjan’s Algorithm:
C++
#include <iostream>
#include <list>
#include <stack>
#define NIL -1
using namespace std;
class Graph {
int V;
list< int >* adj;
void SCCUtil( int u, int disc[],
int low[], stack< int >* st,
bool stackMember[]);
public :
Graph( int V);
void addEdge( int v, int w);
void SCC();
};
Graph::Graph( int V)
{
this ->V = V;
adj = new list< int >[V];
}
void Graph::addEdge( int v, int w)
{
adj[v].push_back(w);
}
void Graph::SCCUtil( int u, int disc[],
int low[], stack< int >* st,
bool stackMember[])
{
static int time = 0;
disc[u] = low[u] = ++ time ;
st->push(u);
stackMember[u] = true ;
list< int >::iterator i;
for (i = adj[u].begin();
i != adj[u].end(); ++i) {
int v = *i;
if (disc[v] == -1) {
SCCUtil(v, disc, low,
st, stackMember);
low[u] = min(low[u], low[v]);
}
else if (stackMember[v] == true )
low[u] = min(low[u], disc[v]);
}
int w = 0;
if (low[u] == disc[u]) {
while (st->top() != u) {
w = ( int )st->top();
cout << w << " " ;
stackMember[w] = false ;
st->pop();
}
w = ( int )st->top();
cout << w << "\n" ;
stackMember[w] = false ;
st->pop();
}
}
void Graph::SCC()
{
int * disc = new int [V];
int * low = new int [V];
bool * stackMember = new bool [V];
stack< int >* st = new stack< int >();
for ( int i = 0; i < V; i++) {
disc[i] = NIL;
low[i] = NIL;
stackMember[i] = false ;
}
for ( int i = 0; i < V; i++) {
if (disc[i] == NIL) {
SCCUtil(i, disc, low,
st, stackMember);
}
}
}
int main()
{
Graph g1(5);
g1.addEdge(1, 0);
g1.addEdge(0, 2);
g1.addEdge(2, 1);
g1.addEdge(0, 3);
g1.addEdge(3, 4);
g1.SCC();
return 0;
}
|
Java
import java.io.*;
import java.util.*;
class GFG {
private int V;
private LinkedList<Integer>[] adj;
private int time;
GFG( int V) {
this .V = V;
adj = new LinkedList[V];
for ( int i = 0 ; i < V; i++) {
adj[i] = new LinkedList<>();
}
}
void addEdge( int v, int w) {
adj[v].add(w);
}
void SCCUtil( int u, int [] disc, int [] low, Stack<Integer> st, boolean [] stackMember) {
disc[u] = low[u] = ++time;
st.push(u);
stackMember[u] = true ;
for ( int v : adj[u]) {
if (disc[v] == - 1 ) {
SCCUtil(v, disc, low, st, stackMember);
low[u] = Math.min(low[u], low[v]);
}
else if (stackMember[v]) {
low[u] = Math.min(low[u], disc[v]);
}
}
if (low[u] == disc[u]) {
int w;
do {
w = st.pop();
System.out.print(w + " " );
stackMember[w] = false ;
} while (w != u);
System.out.println();
}
}
void SCC() {
int [] disc = new int [V];
int [] low = new int [V];
boolean [] stackMember = new boolean [V];
Stack<Integer> st = new Stack<>();
Arrays.fill(disc, - 1 );
Arrays.fill(low, - 1 );
Arrays.fill(stackMember, false );
for ( int i = 0 ; i < V; i++) {
if (disc[i] == - 1 ) {
SCCUtil(i, disc, low, st, stackMember);
}
}
}
}
public class Main {
public static void main(String[] args) {
GFG g1 = new GFG( 5 );
g1.addEdge( 1 , 0 );
g1.addEdge( 0 , 2 );
g1.addEdge( 2 , 1 );
g1.addEdge( 0 , 3 );
g1.addEdge( 3 , 4 );
g1.SCC();
}
}
|
Python3
from collections import defaultdict
class Graph:
def __init__( self , vertices):
self .V = vertices
self .adj = defaultdict( list )
self .time = 0
def add_edge( self , u, v):
self .adj[u].append(v)
def SCCUtil( self , u, disc, low, stackMember, st):
disc[u] = self .time
low[u] = self .time
self .time + = 1
stackMember[u] = True
st.append(u)
for v in self .adj[u]:
if disc[v] = = - 1 :
self .SCCUtil(v, disc, low, stackMember, st)
low[u] = min (low[u], low[v])
elif stackMember[v]:
low[u] = min (low[u], disc[v])
w = - 1
if low[u] = = disc[u]:
while w ! = u:
w = st.pop()
print (w, end = ' ' )
stackMember[w] = False
print ()
def SCC( self ):
disc = [ - 1 ] * self .V
low = [ - 1 ] * self .V
stackMember = [ False ] * self .V
st = []
for i in range ( self .V):
if disc[i] = = - 1 :
self .SCCUtil(i, disc, low, stackMember, st)
g = Graph( 5 )
g.add_edge( 1 , 0 )
g.add_edge( 0 , 2 )
g.add_edge( 2 , 1 )
g.add_edge( 0 , 3 )
g.add_edge( 3 , 4 )
g.SCC()
|
C#
using System;
using System.Collections.Generic;
class Graph
{
private int V;
private List< int >[] adj;
private int time;
public Graph( int vertices)
{
V = vertices;
adj = new List< int >[V];
for ( int i = 0; i < V; i++)
{
adj[i] = new List< int >();
}
time = 0;
}
public void AddEdge( int v, int w)
{
adj[v].Add(w);
}
private void SCCUtil( int u, int [] disc, int [] low, Stack< int > st, bool [] stackMember)
{
disc[u] = low[u] = ++time;
st.Push(u);
stackMember[u] = true ;
foreach ( int v in adj[u])
{
if (disc[v] == -1)
{
SCCUtil(v, disc, low, st, stackMember);
low[u] = Math.Min(low[u], low[v]);
}
else if (stackMember[v])
{
low[u] = Math.Min(low[u], disc[v]);
}
}
if (low[u] == disc[u])
{
while (st.Count > 0)
{
int w = st.Pop();
stackMember[w] = false ;
Console.Write(w);
if (w == u)
{
Console.WriteLine();
break ;
}
else
{
Console.Write( " " );
}
}
}
}
public void SCC()
{
int [] disc = new int [V];
int [] low = new int [V];
bool [] stackMember = new bool [V];
Stack< int > st = new Stack< int >();
for ( int i = 0; i < V; i++)
{
disc[i] = -1;
low[i] = -1;
stackMember[i] = false ;
}
for ( int i = 0; i < V; i++)
{
if (disc[i] == -1)
{
SCCUtil(i, disc, low, st, stackMember);
}
}
}
public static void Main( string [] args)
{
Graph g1 = new Graph(5);
g1.AddEdge(1, 0);
g1.AddEdge(0, 2);
g1.AddEdge(2, 1);
g1.AddEdge(0, 3);
g1.AddEdge(3, 4);
g1.SCC();
}
}
|
Javascript
let NIL = -1
let time = 0;
class Graph {
constructor(V){
this .V = V;
this .adj = new Array(V);
for (let i = 0; i < V; i++){
this .adj[i] = new Array();
}
}
addEdge(v, w)
{
this .adj[v].push(w);
}
SCCUtil(u, disc, low, st, stackMember)
{
disc[u] = low[u] = ++time;
st.push(u);
stackMember[u] = true ;
for (let i = 0; i < this .adj[u].length; i++){
let v = this .adj[u][i];
if (disc[v] == -1) {
this .SCCUtil(v, disc, low, st, stackMember);
low[u] = Math.min(low[u], low[v]);
}
else if (stackMember[v] == true )
low[u] = Math.min(low[u], disc[v]);
}
let w = 0;
if (low[u] == disc[u]) {
while (st[st.length-1] != u) {
w = st[st.length-1];
process.stdout.write(w + " " );
stackMember[w] = false ;
st.pop();
}
w = st[st.length-1];
process.stdout.write(w + "\n" );
stackMember[w] = false ;
st.pop();
}
}
SCC()
{
let disc = new Array( this .V);
let low = new Array( this .V);
let stackMember = new Array( this .V);
let st = [];
for (let i = 0; i < this .V; i++) {
disc[i] = NIL;
low[i] = NIL;
stackMember[i] = false ;
}
for (let i = 0; i < this .V; i++) {
if (disc[i] == NIL) {
this .SCCUtil(i, disc, low, st, stackMember);
}
}
}
};
let g1 = new Graph(5);
g1.addEdge(1, 0);
g1.addEdge(0, 2);
g1.addEdge(2, 1);
g1.addEdge(0, 3);
g1.addEdge(3, 4);
g1.SCC();
|
Kosaraju’s Algorithm: The Kosaraju’s Algorithm is also a Depth First Search based algorithm which is used to find the SCC in a directed graph in linear time complexity. The basic concept of this algorithm is that if we are able to arrive at vertex v initially starting from vertex u, then we should be able to arrive at vertex u starting from vertex v, and if this is the situation, we can say and conclude that vertices u and v are strongly connected, and they are in the strongly connected sub-graph.
Working:
- Perform a DFS traversal on the given graph, keeping track of the finish times of each node. This process can be performed by using a stack.
- When the procedure of running the DFS traversal over the graph finishes, put the source vertex on the stack. In this way, the node with the highest finishing time will be at the top of the stack.
- Reverse the original graph by using an Adjacency List.
- Then perform another DFS traversal on the reversed graph with the source vertex as the vertex on the top of the stack. When the DFS running on the reversed graph finishes, all the nodes that are visited will form one strongly connected component.
- If any more nodes are left or remain unvisited, this signifies the presence of more than one strongly connected component on the graph.
- So pop the vertices from the top of the stack until a valid unvisited node is found. This will have the highest finishing time of all currently unvisited nodes.
Below is the program to find the SCC of the given graph using Kosaraju’s Algorithm:
C++
#include <iostream>
#include <list>
#include <stack>
using namespace std;
class Graph {
int V;
list< int >* adj;
void fillOrder( int v, bool visited[],
stack< int >& Stack);
void DFSUtil( int v, bool visited[]);
public :
Graph( int V);
void addEdge( int v, int w);
void printSCCs();
Graph getTranspose();
};
Graph::Graph( int V)
{
this ->V = V;
adj = new list< int >[V];
}
void Graph::DFSUtil( int v, bool visited[])
{
visited[v] = true ;
cout << v << " " ;
list< int >::iterator i;
for (i = adj[v].begin();
i != adj[v].end(); ++i) {
if (!visited[*i])
DFSUtil(*i, visited);
}
}
Graph Graph::getTranspose()
{
Graph g(V);
for ( int v = 0; v < V; v++) {
list< int >::iterator i;
for (i = adj[v].begin();
i != adj[v].end(); ++i) {
g.adj[*i].push_back(v);
}
}
return g;
}
void Graph::addEdge( int v, int w)
{
adj[v].push_back(w);
}
void Graph::fillOrder( int v, bool visited[],
stack< int >& Stack)
{
visited[v] = true ;
list< int >::iterator i;
for (i = adj[v].begin();
i != adj[v].end(); ++i) {
if (!visited[*i]) {
fillOrder(*i, visited, Stack);
}
}
Stack.push(v);
}
void Graph::printSCCs()
{
stack< int > Stack;
bool * visited = new bool [V];
for ( int i = 0; i < V; i++)
visited[i] = false ;
for ( int i = 0; i < V; i++)
if (visited[i] == false )
fillOrder(i, visited, Stack);
Graph gr = getTranspose();
for ( int i = 0; i < V; i++)
visited[i] = false ;
while (Stack.empty() == false ) {
int v = Stack.top();
Stack.pop();
if (visited[v] == false ) {
gr.DFSUtil(v, visited);
cout << endl;
}
}
}
int main()
{
Graph g(5);
g.addEdge(1, 0);
g.addEdge(0, 2);
g.addEdge(2, 1);
g.addEdge(0, 3);
g.addEdge(3, 4);
g.printSCCs();
return 0;
}
|
Java
import java.util.*;
class Graph {
private int V;
private LinkedList<Integer>[] adj;
Graph( int V) {
this .V = V;
adj = new LinkedList[V];
for ( int i = 0 ; i < V; i++) {
adj[i] = new LinkedList<>();
}
}
void addEdge( int v, int w) {
adj[v].add(w);
}
void DFSUtil( int v, boolean [] visited) {
visited[v] = true ;
System.out.print(v + " " );
for (Integer i : adj[v]) {
if (!visited[i]) {
DFSUtil(i, visited);
}
}
}
Graph getTranspose() {
Graph g = new Graph(V);
for ( int v = 0 ; v < V; v++) {
for (Integer i : adj[v]) {
g.adj[i].add(v);
}
}
return g;
}
void fillOrder( int v, boolean [] visited, Stack<Integer> stack) {
visited[v] = true ;
for (Integer i : adj[v]) {
if (!visited[i]) {
fillOrder(i, visited, stack);
}
}
stack.push(v);
}
void printSCCs() {
Stack<Integer> stack = new Stack<>();
boolean [] visited = new boolean [V];
for ( int i = 0 ; i < V; i++) {
if (!visited[i]) {
fillOrder(i, visited, stack);
}
}
Graph gr = getTranspose();
Arrays.fill(visited, false );
while (!stack.isEmpty()) {
int v = stack.pop();
if (!visited[v]) {
gr.DFSUtil(v, visited);
System.out.println();
}
}
}
}
class Main {
public static void main(String[] args) {
Graph g = new Graph( 5 );
g.addEdge( 1 , 0 );
g.addEdge( 0 , 2 );
g.addEdge( 2 , 1 );
g.addEdge( 0 , 3 );
g.addEdge( 3 , 4 );
g.printSCCs();
}
}
|
Python3
class Graph:
def __init__( self ,vertices):
self .V = vertices
self .adj = [[] for i in range (vertices)]
def addEdge( self ,u,v):
self .adj[u].append(v)
def DFSUtil( self ,v,visited):
visited[v] = True
print (v, end = ' ' )
for i in self .adj[v]:
if visited[i] = = False :
self .DFSUtil(i,visited)
def getTranspose( self ):
g = Graph( self .V)
for v in range ( self .V):
for i in self .adj[v]:
g.adj[i].append(v)
return g
def fillOrder( self ,v,visited,stack):
visited[v] = True
for i in self .adj[v]:
if visited[i] = = False :
self .fillOrder(i,visited,stack)
stack.append(v)
def printSCCs( self ):
stack = []
visited = [ False ] * ( self .V)
for i in range ( self .V):
if visited[i] = = False :
self .fillOrder(i,visited,stack)
gr = self .getTranspose()
visited = [ False ] * ( self .V)
while stack:
i = stack.pop()
if visited[i] = = False :
gr.DFSUtil(i,visited)
print ()
if __name__ = = "__main__" :
g = Graph( 5 )
g.addEdge( 1 , 0 )
g.addEdge( 0 , 2 )
g.addEdge( 2 , 1 )
g.addEdge( 0 , 3 )
g.addEdge( 3 , 4 )
g.printSCCs()
|
C#
using System;
using System.Collections.Generic;
using System.Linq;
class Graph
{
private int V;
private List< int >[] adj;
public Graph( int V)
{
this .V = V;
adj = new List< int >[V];
for ( int i = 0; i < V; i++)
{
adj[i] = new List< int >();
}
}
public void AddEdge( int v, int w)
{
adj[v].Add(w);
}
private void DFSUtil( int v, bool [] visited)
{
visited[v] = true ;
Console.Write(v + " " );
foreach ( int i in adj[v])
{
if (!visited[i])
{
DFSUtil(i, visited);
}
}
}
public Graph GetTranspose()
{
Graph g = new Graph(V);
for ( int v = 0; v < V; v++)
{
foreach ( int i in adj[v])
{
g.adj[i].Add(v);
}
}
return g;
}
private void FillOrder( int v, bool [] visited, Stack< int > stack)
{
visited[v] = true ;
foreach ( int i in adj[v])
{
if (!visited[i])
{
FillOrder(i, visited, stack);
}
}
stack.Push(v);
}
public void PrintSCCs()
{
Stack< int > stack = new Stack< int >();
bool [] visited = new bool [V];
for ( int i = 0; i < V; i++)
{
if (!visited[i])
{
FillOrder(i, visited, stack);
}
}
Graph gr = GetTranspose();
Array.Fill(visited, false );
while (stack.Count > 0)
{
int v = stack.Pop();
if (!visited[v])
{
gr.DFSUtil(v, visited);
Console.WriteLine();
}
}
}
}
class Program
{
public static void Main()
{
Graph g = new Graph(5);
g.AddEdge(1, 0);
g.AddEdge(0, 2);
g.AddEdge(2, 1);
g.AddEdge(0, 3);
g.AddEdge(3, 4);
g.PrintSCCs();
}
}
|
Javascript
class Graph {
constructor(vertices) {
this .V = vertices;
this .adj = new Array(vertices)
.fill()
.map(() => []);
}
addEdge(u, v) {
this .adj[u].push(v);
}
DFSUtil(v, visited) {
visited[v] = true ;
console.log(v);
for (let i of this .adj[v]) {
if (!visited[i]) {
this .DFSUtil(i, visited);
}
}
}
getTranspose() {
const g = new Graph( this .V);
for (let v = 0; v < this .V; v++) {
for (let i of this .adj[v]) {
g.adj[i].push(v);
}
}
return g;
}
fillOrder(v, visited, stack) {
visited[v] = true ;
for (let i of this .adj[v]) {
if (!visited[i]) {
this .fillOrder(i, visited, stack);
}
}
stack.push(v);
}
printSCCs() {
const stack = [];
const visited = new Array( this .V).fill( false );
for (let i = 0; i < this .V; i++) {
if (!visited[i]) {
this .fillOrder(i, visited, stack);
}
}
const gr = this .getTranspose();
visited.fill( false );
while (stack.length > 0) {
const i = stack.pop();
if (!visited[i]) {
gr.DFSUtil(i, visited);
console.log( '' );
}
}
}
}
const g = new Graph(5);
g.addEdge(1, 0);
g.addEdge(0, 2);
g.addEdge(2, 1);
g.addEdge(0, 3);
g.addEdge(3, 4);
g.printSCCs();
|
Time Complexity:
The time complexity of Tarjan’s Algorithm and Kosaraju’s Algorithm will be O(V + E), where V represents the set of vertices and E represents the set of edges of the graph. Tarjan’s algorithm has much lower constant factors w.r.t Kosaraju’s algorithm. In Kosaraju’s algorithm, the traversal of the graph is done at least 2 times, so the constant factor can be of double time. We can print the SCC in progress with Kosaraju’s algorithm as we perform the second DFS. While performing Tarjan’s Algorithm, it requires extra time to print the SCC after finding the head of the SCCs sub-tree.
Summary:
Both the methods have the same linear time complexity, but the techniques or the procedure for the SCC computations are fairly different. Tarjan’s method solely depends on the record of nodes in a DFS to partition the graph whereas Kosaraju’s method performs the two DFS (or 3 DFS if we want to leave the original graph unchanged) on the graph and is quite similar to the method for finding the topological sorting of a graph.
Last Updated :
09 Nov, 2023
Like Article
Save Article
Share your thoughts in the comments
Please Login to comment...