Comparison between Tarjan’s and Kosaraju’s Algorithm


Tarjan’s algorithm is to locate strongly linked components in a directed graph, Robert Tarjan created the graph traversal technique known as Tarjan's algorithm in 1972. Without going over previously processed nodes, it effectively locates and handles each highly related component using a depth-first search strategy and a stack data structure. The algorithm is often employed in computer science and graph theory and has several uses, including algorithm creation, network analysis, and data mining.

Kosaraju’s algorithm consists of two passes over the graph. In the first pass, the graph is traversed in reverse order and a "finish time" is assigned to each node. In the second pass, the nodes are visited in the order of their finish times, and each strongly connected component is identified and marked.

Tarjan’s Algorithm Method

In this case, the Graph class is defined at the beginning of the program, and its constructor initializes an array of adjacency lists based on the number of vertices in the graph. By including the destination vertex in the adjacency list of the source vertex, the addEdge function adds an edge to the graph.

The SCCUtil method, a recursive DFS-based function utilized by the SCC method to discover the SCC, forms the basis of the programme. The current vertex u, an array of discovery times disc, an array of low values low, an array of vertex stacks st, and an array of boolean values stackMember that tracks whether a vertex is in the stack are the inputs for this function.

The procedure puts the current vertex onto the stack and changes its stackMember value to true after first assigning it a discovery time and a low value. After that, it updates the low values of all the nearby vertices to the current vertex in a recursive manner.

The technique iteratively visits undiscovered vertex vs and modifies the low value of u. The method modifies the low value of u based on the discovery time of v if v is already in the stack.

Algorithm Tarjan

  • Initialize the algorithm

  • Start traversing the graph

  • Check for strongly connected components

  • Repeat until all nodes have been visited

  • Return the strongly connected components

The method pops all vertices off the stack until the current vertex u is popped, prints the popped vertices, and sets their stackMember values to false if u is a head node (i.e., if its low value equals its discovery time).

Example

// C++ program to find the SCC using
// Tarjan's algorithm (single DFS)
#include <iostream>
#include <list>
#include <stack>
#define NIL -1
using namespace std;

// A class that represents
// an directed graph
class Graph {
    // No. of vertices
    int V;

    // A dynamic array of adjacency lists
    list<int>* adj;

    // A Recursive DFS based function
    // used by SCC()
    void SCCUtil(int u, int disc[], int low[], stack<int>* st, bool stackMember[]);

    public:
    // Member functions
    Graph(int V);
    void addEdge(int v, int w);
    void SCC();
};

// Constructor
Graph::Graph(int V) {
    this->V = V;
    adj = new list<int>[V];
}

// Function to add an edge to the graph
void Graph::addEdge(int v, int w) {
    adj[v].push_back(w);
}

// Recursive function to finds the SCC
// using DFS traversal
void Graph::SCCUtil(int u, int disc[],  int low[], stack<int>* st,  bool stackMember[]) {
    static int time = 0;

    // Initialize discovery time
    // and low value
    disc[u] = low[u] = ++time;
    st->push(u);
    stackMember[u] = true;

    // Go through all vertices
    // adjacent to this
    list<int>::iterator i;

    for (i = adj[u].begin();
        i != adj[u].end(); ++i) {
        // v is current adjacent of 'u'
        int v = *i;

        // If v is not visited yet,
        // then recur for it
        if (disc[v] == -1) {
           SCCUtil(v, disc, low, st, stackMember);

           // Check if the subtree rooted
           // with 'v' has connection to
           // one of the ancestors of 'u'
            low[u] = min(low[u], low[v]);
        }

        // Update low value of 'u' only of
        // 'v' is still in stack
        else if (stackMember[v] == true)
            low[u] = min(low[u], disc[v]);
    }

    // head node found, pop the stack
    // and print an SCC

    // Store stack extracted vertices
    int w = 0;

    // If low[u] and disc[u]
    if (low[u] == disc[u]) {
    // Until stack st is empty
        while (st->top() != u) {
            w = (int)st->top();

            // Print the node
            cout << w << " ";
            stackMember[w] = false;
            st->pop();
        }
        w = (int)st->top();
        cout << w << "\n";
        stackMember[w] = false;
        st->pop();
    }
}

// Function to find the SCC in the graph
void Graph::SCC() {
    // Stores the discovery times of
    // the nodes
    int* disc = new int[V];

    // Stores the nodes with least
    // discovery time
    int* low = new int[V];

    // Checks whether a node is in
    // the stack or not
    bool* stackMember = new bool[V];

    // Stores all the connected ancestors
    stack<int>* st = new stack<int>();

    // Initialize disc and low,
    // and stackMember arrays
    for (int i = 0; i < V; i++) {
        disc[i] = NIL;
        low[i] = NIL;
        stackMember[i] = false;
    }

    // Recursive helper function to
    // find the SCC in DFS tree with
    // vertex 'i'
    for (int i = 0; i < V; i++) {

        // If current node is not
        // yet visited
        if (disc[i] == NIL) {
            SCCUtil(i, disc, low, st, stackMember);
        }
    }
}

// Driver Code
int main() {
    // Given a graph
    Graph g1(5);
    g1.addEdge(3, 5);
    g1.addEdge(0, 2);
    g1.addEdge(2, 1);
    g1.addEdge(4, 3);
    g1.addEdge(3, 4);

    // Function Call to find SCC using
    // Tarjan's Algorithm
    g1.SCC();

    return 0;
} 

Output

1
2
0
4 3 

Method 2- Kosaraju

Algorithm of Kosaraju

  • Create a collection of visited nodes and an empty stack upon startup.

  • Start a depth-first search from the first node in the graph that hasn't been visited for each node. Push each node that was visited throughout the search onto the stack.

  • Every graph edge should have its orientation reversed.

  • Pop a node off the stack if it is still filled with nodes. Perform a depth-first search from the node if it hasn't been accessed. Mark each node that was visited during the search as a member of the current highly linked component.

  • Till all nodes have been visited, repeat

  • .
  • Each highly linked component will be recognised and noted at the conclusion of the procedure.

The below C++ code uses Kosaraju's Algorithm to find the Strongly Connected Components (SCCs) in a directed graph. The software defines a class called Graph, which has the following member functions:

Graph(int V): Constructor that takes an input of the number of vertices V and initialises the adjacency list of the graph.

Void addEdge(int v, int w): Using two integers, v and w, as inputs, this method creates an edge connecting vertex v to vertex w in the graph.

Kosaraju's Algorithm is used by the void printSCCs() function to print each SCC in the graph.

The method Graph getTranspose() offers the transposition of the graph.

Vertices are added to a stack in the order of their completing times using the recursive function void fillOrder(int v, bool visited[, stack& Stack], int v).

Example-2

// C++ program to print the SCC of the 
// graph using Kosaraju's Algorithm 
#include <iostream> 
#include <list>
#include <stack> 
using namespace std; 
 
class Graph {
   // No. of vertices 
   int V; 
    
   // An array of adjacency lists 
   list<int>* adj; 
    
   // Member Functions 
   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(); 
}; 
 
// Constructor of class 
Graph::Graph(int V) { 
   this->V = V; 
   adj = new list<int>[V]; 
} 
 
// Recursive function to print DFS 
// starting from v 
void Graph::DFSUtil(int v, bool visited[])  { 
   // Mark the current node as 
   // visited and print it 
   visited[v] = true; 
   cout << v <<" "; 
    
   // Recur for all the vertices 
   // adjacent to this vertex 
   list<int>::iterator i; 
    
   // Traverse Adjacency List of node v 
   for (i = adj[v].begin(); 
   i != adj[v].end(); ++i) { 
       
      // If child node *i is unvisited 
      if (!visited[*i]) 
      DFSUtil(*i, visited); 
   } 
} 
 
// Function to get the transpose of 
// the given graph 
Graph Graph::getTranspose()  { 
   Graph g(V); 
   for (int v = 0; v < V; v++) { 
      // Recur for all the vertices 
      // adjacent to this vertex 
      list<int>::iterator i; 
      for (i = adj[v].begin(); 
      i != adj[v].end(); ++i) { 
         // Add to adjacency list 
         g.adj[*i].push_back(v); 
      } 
   } 
    
   // Return the reversed graph 
   return g; 
} 
 
// Function to add an Edge to the given 
// graph 
void Graph::addEdge(int v, int w)  { 
   // Add w to v’s list 
   adj[v].push_back(w); 
} 
 
// Function that fills stack with vertices 
// in increasing order of finishing times 
void Graph::fillOrder(int v, bool visited[], 
stack<int>& Stack)  { 
   // Mark the current node as 
   // visited and print it 
   visited[v] = true; 
    
   // Recur for all the vertices 
   // adjacent to this vertex 
   list<int>::iterator i; 
    
   for (i = adj[v].begin(); 
   i != adj[v].end(); ++i) { 
    
      // If child node *i is unvisited 
      if (!visited[*i]) { 
         fillOrder(*i, visited, Stack); 
      } 
   } 
    
   // All vertices reachable from v 
   // are processed by now, push v 
   Stack.push(v); 
} 
 
// Function that finds and prints all 
// strongly connected components 
void Graph::printSCCs()  { 
   stack<int> Stack; 
    
   // Mark all the vertices as 
   // not visited (For first DFS) 
   bool* visited = new bool[V]; 
   for (int i = 0; i < V; i++) 
   visited[i] = false; 
    
   // Fill vertices in stack according 
   // to their finishing times 
   for (int i = 0; i < V; i++) 
   if (visited[i] == false) 
   fillOrder(i, visited, Stack); 
    
   // Create a reversed graph 
   Graph gr = getTranspose(); 
    
   // Mark all the vertices as not 
   // visited (For second DFS) 
   for (int i = 0; i < V; i++) 
   visited[i] = false; 
    
   // Now process all vertices in 
   // order defined by Stack 
   while (Stack.empty() == false) { 
    
      // Pop a vertex from stack 
      int v = Stack.top(); 
      Stack.pop(); 
       
      // Print SCC of the popped vertex 
      if (visited[v] == false) { 
         gr.DFSUtil(v, visited); 
         cout << endl; 
      } 
   } 
} 
 
// Driver Code 
int main()  { 
   // Given Graph 
   Graph g(5); 
   g.addEdge(1, 0); 
   g.addEdge(0, 2); 
   g.addEdge(2, 1); 
   g.addEdge(1, 3); 
   g.addEdge(2, 4); 
    
   // Function Call to find the SCC 
   // using Kosaraju's Algorithm 
   g.printSCCs(); 
    
   return 0; 
} 

Output

0 1 2 
4
3

Conclusion

In summary, The temporal complexity of Tarjan's and Kosaraju's methods is O(V + E), where V denotes the set of vertices and E denotes the set of edges in a graph. The constant factors in Tarjan's algorithm are significantly lower than those in Kosaraju's approach. Kosaraju's method traverses the graph at least twice, hence the constant factor may be double-time. We may use Kosaraju's approach to produce the SCC that is now active while we finish the second DFS. Once the head of the SCCs sub-tree has been found, printing the SCC with Tarjan's Algorithm takes longer.

Although the linear time complexity of the two approaches is identical, there are some differences in the methodology or processes used for SCC calculations. While Kosaraju's technique runs two DFS (or three DFS if we wish to keep the original graph unmodified) on the graph and is pretty similar to the method for determining the topological ordering of a graph, Tarjan's method just relies on the record of nodes in a DFS to partition the graph.

Updated on: 21-Jul-2023

263 Views

Kickstart Your Career

Get certified by completing the course

Get Started
Advertisements