In a directed graph, a Strongly Connected Component is a subset of vertices where every vertex in the subset is reachable from every other vertex in the same subset by traversing the directed edges. Finding the SCCs of a graph can provide important insights into the structure and connectivity of the graph, with applications in various fields such as social network analysis, web crawling, and network routing.
The below graph has two strongly connected components {1,2,3,4} and {5,6,7} since there is path from each vertex to every other vertex in the same strongly connected component.
Strongly Connected Components
Difference Between Connected and Strongly Connected Components (SCCs) : The term Connected is for undirected graphs and Strongly Connected is for directed graphs. A subgraph of a directed graph is considered to be an Strongly Connected Components(SCC) if and only if for every pair of vertices A and B, there exists a path from A to B and a path from B to A.
Connecting Two Strongly Connected Component by a Unidirectional Edge : Two different connected components becomes a single component if a edge is added between a vertex from one component to a vertex of other component. But this is not the case in strongly connected components. Two strongly connected components d not become a single strongly connected component if there is only a unidirectional edge from one SCC to other SCC. Please refer the above graph for example. We have edges from 4 to 5 and 3 to 4, despite these edges, two components are different.
Example:
Input: adj[][] = [[], [3, 4], [1], [2], [5], []]
Output: [[1, 2, 3], [4], [5]] Explanation: There are 3 different Strongly Connected Components. They are {1, 2, 3} and {4}, {5}.
The main idea will be for each vertex i, find the vertices which will be the part of strongly connected component containing vertex i. Two vertex i and j will be in the same strongly connected component if they there is a directed path from vertex i to vertex j and vice-versa.
Let's understand the approach with the help of following example:
Brute Force Approach
Starting with vertex 1. There is path from vertex 1 to vertex 2 and vice-versa. Similarly there is a path from vertex 1 to vertex 3 and vice versa. So, vertex 2 and 3 will be in the same Strongly Connected Component as vertex 1. Although there is directed path form vertex 1 to vertex 4 and vertex 5. But there is no directed path from vertex 4,5 to vertex 1 so vertex 4 and 5 won't be in the same Strongly Connected Component as vertex 1. Thus Vertex 1,2 and 3 forms a Strongly Connected Component.
For Vertex 2 and 3, there Strongly Connected Component has already been determined.
For Vertex 4, there is a path from vertex 4 to vertex 5 but there is no path from vertex 5 to vertex 4. So vertex 4 and 5 won't be in the Same Strongly Connected Component. So both Vertex 4 and Vertex 5 will be part of Single Strongly Connected Component.
Hence there will be 3 Strongly Connected Component {1,2,3}, {4} and {5}.
Below is the implementation of above approach:
C++
#include<iostream>#include<vector>#include<algorithm>usingnamespacestd;// dfs Function to reach destinationbooldfs(intcurr,intdes,vector<vector<int>>&adj,vector<int>&vis){// If curr node is destination return trueif(curr==des){returntrue;}vis[curr]=1;for(autox:adj[curr]){if(!vis[x]){if(dfs(x,des,adj,vis)){returntrue;}}}returnfalse;}// To tell whether there is path from source to// destinationboolisPath(intsrc,intdes,vector<vector<int>>&adj){vector<int>vis(adj.size()+1,0);returndfs(src,des,adj,vis);}// Function to return all the strongly connected// component of a graph.vector<vector<int>>findSCC(intn,vector<vector<int>>&a){// Stores all the strongly connected components.vector<vector<int>>ans;// Stores whether a vertex is a part of any Strongly// Connected Componentvector<int>is_scc(n+1,0);vector<vector<int>>adj(n+1);for(inti=0;i<a.size();i++){adj[a[i][0]].push_back(a[i][1]);}// Traversing all the verticesfor(inti=1;i<=n;i++){if(!is_scc[i]){// If a vertex i is not a part of any SCC// insert it into a new SCC list and check// for other vertices whether they can be// thr part of thidl ist.vector<int>scc;scc.push_back(i);for(intj=i+1;j<=n;j++){// If there is a path from vertex i to// vertex j and vice versa put vertex j// into the current SCC list.if(!is_scc[j]&&isPath(i,j,adj)&&isPath(j,i,adj)){is_scc[j]=1;scc.push_back(j);}}// Insert the SCC containing vertex i into// the final list.ans.push_back(scc);}}returnans;}intmain(){intV=5;vector<vector<int>>edges{{1,3},{1,4},{2,1},{3,2},{4,5}};vector<vector<int>>ans=findSCC(V,edges);cout<<"Strongly Connected Components are:\n";for(inti=0;i<ans.size();i++){for(intj=0;j<ans[i].size();j++){cout<<ans[i][j]<<" ";}cout<<"\n";}}
Java
classGfG{// dfs Function to reach destinationbooleandfs(intcurr,intdes,int[][]adj,int[]vis,intn){if(curr==des)returntrue;vis[curr]=1;for(inti=1;i<=n;i++){if(adj[curr][i]==1&&vis[i]==0){if(dfs(i,des,adj,vis,n))returntrue;}}returnfalse;}// To tell whether there is path from source to destinationbooleanisPath(intsrc,intdes,int[][]adj,intn){int[]vis=newint[n+1];returndfs(src,des,adj,vis,n);}// Function to return all the strongly connected component of a graph.int[][]findSCC(intn,int[][]a,intm){// Stores whether a vertex is part of any SCCint[]is_scc=newint[n+1];// Adjacency matrix representation of graphint[][]adj=newint[n+1][n+1];for(inti=0;i<m;i++){intu=a[i][0];intv=a[i][1];adj[u][v]=1;}// Result array to store all SCCs (assume worst-case n SCCs of size n)int[][]result=newint[n][n+1];// +1 to store count at index 0intsccCount=0;// Traversing all the verticesfor(inti=1;i<=n;i++){if(is_scc[i]==0){int[]scc=newint[n+1];intsize=0;scc[++size]=i;for(intj=i+1;j<=n;j++){if(is_scc[j]==0&&isPath(i,j,adj,n)&&isPath(j,i,adj,n)){is_scc[j]=1;scc[++size]=j;}}// Mark current node as visited in SCCis_scc[i]=1;// Store size at index 0 and then elementsscc[0]=size;result[sccCount++]=scc;}}// Trim result to valid SCCs onlyint[][]finalResult=newint[sccCount][];for(inti=0;i<sccCount;i++){intsize=result[i][0];finalResult[i]=newint[size];for(intj=0;j<size;j++){finalResult[i][j]=result[i][j+1];}}returnfinalResult;}}publicclassMain{publicstaticvoidmain(String[]args){GfGobj=newGfG();intV=5;int[][]edges={{1,3},{1,4},{2,1},{3,2},{4,5}};intM=edges.length;int[][]ans=obj.findSCC(V,edges,M);System.out.println("Strongly Connected Components are:");for(int[]scc:ans){for(intx:scc){System.out.print(x+" ");}System.out.println();}}}
Python
classGfG:# dfs Function to reach destinationdefdfs(self,curr,des,adj,vis):# If current node is the destination, return Trueifcurr==des:returnTruevis[curr]=1forxinadj[curr]:ifnotvis[x]:ifself.dfs(x,des,adj,vis):returnTruereturnFalse# To tell whether there is a path from source to destinationdefisPath(self,src,des,adj):vis=[0]*(len(adj)+1)returnself.dfs(src,des,adj,vis)# Function to return all the strongly connected components of a graph.deffindSCC(self,n,a):# Stores all the strongly connected components.ans=[]# Stores whether a vertex is a part of any Strongly Connected Componentis_scc=[0]*(n+1)adj=[[]for_inrange(n+1)]foriinrange(len(a)):adj[a[i][0]].append(a[i][1])# Traversing all the verticesforiinrange(1,n+1):ifnotis_scc[i]:# If a vertex i is not a part of any SCC, insert it into a new SCC list# and check for other vertices whether they can be part of this list.scc=[i]forjinrange(i+1,n+1):# If there is a path from vertex i to vertex j and vice versa,# put vertex j into the current SCC list.ifnotis_scc[j]andself.isPath(i,j,adj)andself.isPath(j,i,adj):is_scc[j]=1scc.append(j)# Insert the SCC containing vertex i into the final list.ans.append(scc)returnansif__name__=="__main__":obj=GfG()V=5edges=[[1,3],[1,4],[2,1],[3,2],[4,5]]ans=obj.findSCC(V,edges)print("Strongly Connected Components are:")forxinans:foryinx:print(y,end=" ")print()
C#
usingSystem;classGfG{// DFS function to reach destinationpublicbooldfs(intcurr,intdes,int[,]adj,int[]vis,intn){if(curr==des)returntrue;vis[curr]=1;for(inti=1;i<=n;i++){if(adj[curr,i]==1&&vis[i]==0){if(dfs(i,des,adj,vis,n))returntrue;}}returnfalse;}// To tell whether there is a path from source to destinationpublicboolisPath(intsrc,intdes,int[,]adj,intn){int[]vis=newint[n+1];returndfs(src,des,adj,vis,n);}// Function to return all the strongly connected components of a graphpublicint[][]findSCC(intn,int[,]edges,intm){int[,]adj=newint[n+1,n+1];for(inti=0;i<m;i++){intu=edges[i,0];intv=edges[i,1];adj[u,v]=1;}int[]isScc=newint[n+1];int[][]result=newint[n][];intsccIndex=0;for(inti=1;i<=n;i++){if(isScc[i]==0){int[]temp=newint[n+1];// [0] stores sizeintsize=0;temp[++size]=i;for(intj=i+1;j<=n;j++){if(isScc[j]==0&&isPath(i,j,adj,n)&&isPath(j,i,adj,n)){isScc[j]=1;temp[++size]=j;}}isScc[i]=1;temp[0]=size;int[]scc=newint[size];for(intk=0;k<size;k++){scc[k]=temp[k+1];}result[sccIndex++]=scc;}}// Trim result to actual number of SCCsint[][]finalResult=newint[sccIndex][];for(inti=0;i<sccIndex;i++){finalResult[i]=result[i];}returnfinalResult;}}classProgram{staticvoidMain(){GfGobj=newGfG();intV=5;int[,]edges={{1,3},{1,4},{2,1},{3,2},{4,5}};intM=edges.GetLength(0);int[][]ans=obj.findSCC(V,edges,M);Console.WriteLine("Strongly Connected Components are:");foreach(int[]sccinans){foreach(intnodeinscc){Console.Write(node+" ");}Console.WriteLine();}}}
JavaScript
classGfG{// Function to reach the destination using DFSdfs(curr,des,adj,vis){// If the current node is the destination, return trueif(curr===des){returntrue;}vis[curr]=1;for(letxofadj[curr]){if(!vis[x]){if(this.dfs(x,des,adj,vis)){returntrue;}}}returnfalse;}// Check whether there is a path from source to destinationisPath(src,des,adj){constvis=newArray(adj.length+1).fill(0);returnthis.dfs(src,des,adj,vis);}// Function to find all strongly connected components of a graphfindSCC(n,a){// Stores all strongly connected componentsconstans=[];// Stores whether a vertex is part of any Strongly Connected Componentconstis_scc=newArray(n+1).fill(0);constadj=newArray(n+1).fill().map(()=>[]);for(leti=0;i<a.length;i++){adj[a[i][0]].push(a[i][1]);}// Traversing all the verticesfor(leti=1;i<=n;i++){if(!is_scc[i]){// If a vertex i is not part of any SCC,// insert it into a new SCC list and check// for other vertices that can be part of this list.constscc=[i];for(letj=i+1;j<=n;j++){// If there is a path from vertex i to// vertex j and vice versa, put vertex j// into the current SCC list.if(!is_scc[j]&&this.isPath(i,j,adj)&&this.isPath(j,i,adj)){is_scc[j]=1;scc.push(j);}}// Insert the SCC containing vertex i into the final list.ans.push(scc);}}returnans;}}constobj=newGfG();constV=5;constedges=[[1,3],[1,4],[2,1],[3,2],[4,5]];constans=obj.findSCC(V,edges);console.log("Strongly Connected Components are:");for(letxofans){console.log(x.join(" "));}
Output
Strongly Connected Components are:
1 2 3
4
5
Time complexity: O((n^2) * (n + m)), because for each pair of vertices we are checking whether a path exists between them. Auxiliary Space: O(N)
[Expected Approach 1] Using Kasaraju's Algorithm
Performing Depth-First Search (DFS) on the Original Graph:
We first do a DFS on the original graph and record the finish times of nodes (i.e., the time at which the DFS finishes exploring a node completely).
Performing DFS on the Transposed Graph:
We then reverse the direction of all edges in the graph to create the transposed graph.
Next, we perform a DFS on the transposed graph, considering nodes in decreasing order of their finish times recorded in the first phase.
Each DFS traversal in this phase will give us one SCC.
Let us take the following graph as an example. It has 3 strongly connected components.
Connected Components
How does this work? The above algorithm does DFS two times. DFS of a graph produces a single tree if all vertices are reachable from the DFS starting point. Otherwise DFS produces a forest. So DFS of a graph with only one SCC always produces a tree. The important point to note is DFS may produce a tree or a forest when there are more than one SCCs depending upon the chosen starting point. For example, in the above diagram, if we start DFS from vertices 0 or 1 or 2, we get a tree as output. And if we start from 3 or 4, we get a forest. To find and print all SCCs, we would want to start DFS from vertex 4 (which is a sink vertex), then move to 3 which is sink in the remaining set (set excluding 4) and finally any of the remaining vertices (0, 1, 2). So how do we find this sequence of picking vertices as starting points of DFS? Unfortunately, there is no direct way for getting this sequence. However, if we do a DFS of graph and store vertices according to their finish times, we make sure that the finish time of a vertex that connects to other SCCs (other that its own SCC), will always be greater than finish time of vertices in the other SCC.
For example, in DFS of above example graph, finish time of 0 is always greater than 3 and 4 (irrespective of the sequence of vertices considered for DFS). And finish time of 3 is always greater than 4. DFS doesn't guarantee about other vertices, for example finish times of 1 and 2 may be smaller or greater than 3 and 4 depending upon the sequence of vertices considered for DFS. So to use this property, we do DFS traversal of complete graph and push every finished vertex to a stack. In stack, 3 always appears after 4, and 0 appear after both 3 and 4. In the next step, we reverse the graph.
Consider the graph of SCCs. In the reversed graph, the edges that connect two components are reversed. So the SCC {0, 1, 2} becomes sink and the SCC {4} becomes source. As discussed above, in stack, we always have 0 before 3 and 4. So if we do a DFS of the reversed graph using sequence of vertices in stack, we process vertices from sink to source (in reversed graph). That is what we wanted to achieve and that is all needed to print SCCs one by one.
Graph of SCCsC++
#include<iostream>#include<vector>#include<stack>usingnamespacestd;classGfG{public:// Run a dfs on the original graphvoidDFS1(intu,vector<vector<int>>&adj,vector<bool>&visited,stack<int>&st){visited[u]=true;for(intv:adj[u]){if(!visited[v])DFS1(v,adj,visited,st);}st.push(u);}// DFS on reversed graph to collect SCCvoidDFS2(intu,vector<vector<int>>&revAdj,vector<bool>&visited,vector<int>&scc){visited[u]=true;scc.push_back(u);for(intv:revAdj[u]){if(!visited[v])DFS2(v,revAdj,visited,scc);}}vector<vector<int>>kosaraju(intV,vector<vector<int>>&adj){vector<bool>visited(V,false);stack<int>st;// Fill stack with finish time orderfor(inti=0;i<V;i++){if(!visited[i])DFS1(i,adj,visited,st);}// Reverse the graphvector<vector<int>>revAdj(V);for(intu=0;u<V;u++){for(intv:adj[u]){revAdj[v].push_back(u);}}// Process reversed graph in order of stackfill(visited.begin(),visited.end(),false);vector<vector<int>>SCCs;while(!st.empty()){intu=st.top();st.pop();if(!visited[u]){vector<int>scc;DFS2(u,revAdj,visited,scc);SCCs.push_back(scc);}}returnSCCs;}};intmain(){GfGobj;intV=5;vector<vector<int>>edges{{1,3},{1,4},{2,1},{3,2},{4,5}};vector<vector<int>>adj(V+1);for(inti=0;i<edges.size();i++){intu=edges[i][0];intv=edges[i][1];adj[u].push_back(v);}vector<vector<int>>SCCs=obj.kosaraju(V+1,adj);cout<<"Strongly Connected Components:\n";for(inti=0;i<SCCs.size()-1;i++){for(intnode:SCCs[i]){cout<<node<<" ";}cout<<"\n";}return0;}
Java
importjava.util.Stack;classGfG{// Run a dfs on the original graphvoidDFS1(intu,int[][]adj,boolean[]visited,Stack<Integer>st){visited[u]=true;for(intv:adj[u]){if(v==-1)break;// stop at -1 (our custom end marker)if(!visited[v])DFS1(v,adj,visited,st);}st.push(u);}// DFS on reversed graph to collect SCCvoidDFS2(intu,int[][]revAdj,boolean[]visited,int[]scc,int[]idx){visited[u]=true;scc[idx[0]++]=u;for(intv:revAdj[u]){if(v==-1)break;if(!visited[v])DFS2(v,revAdj,visited,scc,idx);}}int[][]kosaraju(intV,int[][]adj){boolean[]visited=newboolean[V];Stack<Integer>st=newStack<>();// Fill stack with finish time orderfor(inti=0;i<V;i++){if(!visited[i])DFS1(i,adj,visited,st);}// Reverse the graphint[][]revAdj=newint[V][V];for(inti=0;i<V;i++){for(intj=0;j<V;j++)revAdj[i][j]=-1;}int[]count=newint[V];for(intu=0;u<V;u++){for(intv:adj[u]){if(v==-1)break;revAdj[v][count[v]++]=u;}}// Process reversed graph in order of stackfor(inti=0;i<V;i++)visited[i]=false;int[][]SCCs=newint[V][V];for(inti=0;i<V;i++){for(intj=0;j<V;j++)SCCs[i][j]=-1;}intsccCount=0;while(!st.isEmpty()){intu=st.pop();if(!visited[u]){int[]scc=newint[V];for(inti=0;i<V;i++)scc[i]=-1;int[]idx={0};DFS2(u,revAdj,visited,scc,idx);SCCs[sccCount++]=scc;}}int[][]result=newint[sccCount][];for(inti=0;i<sccCount;i++)result[i]=SCCs[i];returnresult;}staticint[][]buildAdjMatrix(int[][]edges,intV){int[][]adj=newint[V+1][V+1];for(inti=0;i<=V;i++){for(intj=0;j<=V;j++){adj[i][j]=-1;}}int[]count=newint[V+1];for(int[]edge:edges){intu=edge[0],v=edge[1];adj[u][count[u]++]=v;}returnadj;}publicstaticvoidmain(String[]args){GfGobj=newGfG();intV=5;int[][]edges={{1,3},{1,4},{2,1},{3,2},{4,5}};int[][]adj=buildAdjMatrix(edges,V);int[][]SCCs=obj.kosaraju(V+1,adj);System.out.println("Strongly Connected Components:");for(inti=0;i<SCCs.length-1;i++){for(intj=0;j<SCCs[i].length&&SCCs[i][j]!=-1;j++){System.out.print(SCCs[i][j]+" ");}System.out.println();}}}
Python
fromcollectionsimportdefaultdictclassGfG:# Run a dfs on the original graphdefDFS1(self,u,adj,visited,st):visited[u]=Trueforvinadj[u]:ifnotvisited[v]:self.DFS1(v,adj,visited,st)st.append(u)# DFS on reversed graph to collect SCCdefDFS2(self,u,revAdj,visited,scc):visited[u]=Truescc.append(u)forvinrevAdj[u]:ifnotvisited[v]:self.DFS2(v,revAdj,visited,scc)defkosaraju(self,V,adj):visited=[False]*Vst=[]# Fill stack with finish time orderforiinrange(V):ifnotvisited[i]:self.DFS1(i,adj,visited,st)# Reverse the graphrevAdj=[[]for_inrange(V)]foruinrange(V):forvinadj[u]:revAdj[v].append(u)# Process reversed graph in order of stackvisited=[False]*VSCCs=[]whilest:u=st.pop()ifnotvisited[u]:scc=[]self.DFS2(u,revAdj,visited,scc)SCCs.append(scc)returnSCCsif__name__=="__main__":obj=GfG()V=5edges=[[1,3],[1,4],[2,1],[3,2],[4,5]]adj=[[]for_inrange(V+1)]foru,vinedges:adj[u].append(v)SCCs=obj.kosaraju(V+1,adj)print("Strongly Connected Components:")foriinrange(len(SCCs)-1):fornodeinSCCs[i]:print(node,end=" ")print()
C#
usingSystem;classGfG{// Run a dfs on the original graphvoidDFS1(intu,int[][]adj,bool[]visited,int[]stack,refinttop){visited[u]=true;foreach(intvinadj[u]){if(v==-1)break;if(!visited[v])DFS1(v,adj,visited,stack,reftop);}stack[++top]=u;}// DFS on reversed graph to collect SCCvoidDFS2(intu,int[][]revAdj,bool[]visited,int[]scc,refintidx){visited[u]=true;scc[idx++]=u;foreach(intvinrevAdj[u]){if(v==-1)break;if(!visited[v])DFS2(v,revAdj,visited,scc,refidx);}}publicint[][]kosaraju(intV,int[][]adj){bool[]visited=newbool[V];int[]stack=newint[V];inttop=-1;// Fill stack with finish time orderfor(inti=0;i<V;i++){if(!visited[i])DFS1(i,adj,visited,stack,reftop);}// Reverse the graphint[][]revAdj=newint[V][];int[]revCount=newint[V];for(inti=0;i<V;i++){revAdj[i]=newint[V];for(intj=0;j<V;j++)revAdj[i][j]=-1;}for(intu=0;u<V;u++){foreach(intvinadj[u]){if(v==-1)break;revAdj[v][revCount[v]++]=u;}}// Process reversed graph in order of stackfor(inti=0;i<V;i++)visited[i]=false;int[][]SCCs=newint[V][];for(inti=0;i<V;i++){SCCs[i]=newint[V];for(intj=0;j<V;j++)SCCs[i][j]=-1;}intsccCount=0;while(top>=0){intu=stack[top--];if(!visited[u]){int[]scc=newint[V];for(inti=0;i<V;i++)scc[i]=-1;intidx=0;DFS2(u,revAdj,visited,scc,refidx);SCCs[sccCount++]=scc;}}int[][]result=newint[sccCount][];for(inti=0;i<sccCount;i++)result[i]=SCCs[i];returnresult;}staticint[][]BuildAdjMatrix(int[][]edges,intV){int[][]adj=newint[V+1][];int[]count=newint[V+1];for(inti=0;i<=V;i++){adj[i]=newint[V+1];for(intj=0;j<=V;j++)adj[i][j]=-1;}foreach(int[]edgeinedges){intu=edge[0],v=edge[1];adj[u][count[u]++]=v;}returnadj;}publicstaticvoidMain(){GfGobj=newGfG();intV=5;int[][]edges=newint[][]{newint[]{1,3},newint[]{1,4},newint[]{2,1},newint[]{3,2},newint[]{4,5}};int[][]adj=BuildAdjMatrix(edges,V);int[][]SCCs=obj.kosaraju(V+1,adj);Console.WriteLine("Strongly Connected Components:");for(inti=0;i<SCCs.Length-1;i++){for(intj=0;j<SCCs[i].Length&&SCCs[i][j]!=-1;j++){Console.Write(SCCs[i][j]+" ");}Console.WriteLine();}}}
JavaScript
classGfG{// DFS on original graph to fill the stack with finishing timesDFS1(u,adj,visited,st){visited[u]=true;for(letvofadj[u]){if(!visited[v])this.DFS1(v,adj,visited,st);}st.push(u);}// DFS on reversed graph to collect SCCDFS2(u,revAdj,visited,scc){visited[u]=true;scc.push(u);for(letvofrevAdj[u]){if(!visited[v])this.DFS2(v,revAdj,visited,scc);}}kosaraju(V,adj){letvisited=Array(V).fill(false);letst=[];// Fill stack with finish time orderfor(leti=0;i<V;i++){if(!visited[i])this.DFS1(i,adj,visited,st);}// Reverse the graphletrevAdj=Array.from({length:V},()=>[]);for(letu=0;u<V;u++){for(letvofadj[u]){revAdj[v].push(u);}}// Process reversed graph in order of stackvisited.fill(false);letSCCs=[];while(st.length>0){letu=st.pop();if(!visited[u]){letscc=[];this.DFS2(u,revAdj,visited,scc);SCCs.push(scc);}}returnSCCs;}}(function(){constobj=newGfG();constV=5;constedges=[[1,3],[1,4],[2,1],[3,2],[4,5]];constadj=Array.from({length:V+1},()=>[]);for(let[u,v]ofedges){adj[u].push(v);}constSCCs=obj.kosaraju(V+1,adj);console.log("Strongly Connected Components:");for(leti=0;i<SCCs.length-1;i++){console.log(SCCs[i].join(" "));}})();
Output
Strongly Connected Components:
1 2 3
4
5
Time Complexity - O(V + E) Auxiliary Space - O(V + E)
[Expected Approach 2] Using Tarjan's Algorithm
Tarjan's Algorithm is more efficient because it finds SCCs in a single DFS pass using a stack and some additional bookkeeping:
DFS Traversal: During the DFS, maintain an index for each node and the smallest index (low-link value) that can be reached from the node.
Stack: Keep track of nodes currently in the recursion stack (part of the current SCC being explored).
Identifying SCCs: When a node’s low-link value equals its index, it means we have found an SCC. Pop all nodes from the stack until we reach the current node.