Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                

Strongly Connected Components



Strongly Connected Components (SCCs) are specific sub-graphs in a directed graph where every node can reach every other node within that sub-graph. In simple terms, SCCs are clusters of nodes that are tightly connected but are separate from other parts of the graph. Understanding SCCs is important, as they help in detecting cycles and studying network structures.

A directed graph is said to be strongly connected if every node can reach every other node directly or indirectly. However, in larger graphs, we may have multiple strongly connected parts that are not connected to each other. These independent sections are called strongly connected components.

For example, consider a directed graph with nodes A, B, C, D, and E. If A can reach B and B can reach A, but C, D, and E form a separate cluster where they can all reach each other but not A or B, then there are two SCCs in the graph: {A, B} and {C, D, E}.

Strongly Connected Components Algorithm

There are two popular algorithms to find strongly connected components in a graph:

  • Kosaraju's Algorithm
  • Tarjan's Algorithm

Kosaraju's Algorithm

Kosaraju's Algorithm is a two-pass algorithm that finds strongly connected components in a directed graph. Here's how it works:

  • First Pass: Perform a Depth First Search (DFS) on the graph and store the order of nodes based on their finishing times.
  • Second Pass: Reverse the graph and perform another DFS on the nodes in the order of their finishing times from the first pass. This will give us the strongly connected components.

Kosaraju's Algorithm Example

Let's consider a directed graph with nodes A, B, C, D, and E, and the following edges:

   A -> B
   B -> C
   C -> A
   C -> D
   D -> E
   E -> D

Using Kosaraju's Algorithm, we can find the strongly connected components in the graph:

  • First Pass: The finishing times of the nodes are E, D, C, B, A.
  • Second Pass: The strongly connected components are {A, B, C} and {D, E}.

Code for Kosaraju's Algorithm

Here's an example code snippet for Kosaraju's Algorithm in C, C++, Java, and Python:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAX_VERTICES 5

// Graph adjacency matrix
int adj[MAX_VERTICES][MAX_VERTICES] = {
   {0, 1, 0, 0, 0},
   {0, 0, 1, 0, 0},
   {1, 0, 0, 1, 0},
   {0, 0, 0, 0, 1},
   {0, 0, 0, 1, 0}
};

int visited[MAX_VERTICES] = {0};
int finishing_time[MAX_VERTICES];
int finish_index = 0;

// Mapping index to letters (A-E)
char map[MAX_VERTICES] = {'A', 'B', 'C', 'D', 'E'};

void DFS(int vertex, int print) {
   visited[vertex] = 1;
   if (print) printf("%c ", map[vertex]); // Print vertex if it's in SCC traversal

   for (int v = 0; v < MAX_VERTICES; v++) {
      if (adj[vertex][v] && !visited[v]) {
         DFS(v, print);
      }
   }

   if (!print) finishing_time[finish_index++] = vertex; // Store finishing time if not printing SCC
}

void reverse_graph() {
   int temp[MAX_VERTICES][MAX_VERTICES];

   for (int i = 0; i < MAX_VERTICES; i++) {
      for (int j = 0; j < MAX_VERTICES; j++) {
         temp[i][j] = adj[i][j];
      }
   }

   // Transpose the graph
   for (int i = 0; i < MAX_VERTICES; i++) {
      for (int j = 0; j < MAX_VERTICES; j++) {
         adj[i][j] = temp[j][i];
      }
   }
}

void find_scc() {
   // Step 1: Compute finishing times in the original graph
   for (int v = 0; v < MAX_VERTICES; v++) {
      if (!visited[v]) {
         DFS(v, 0); // First pass DFS (store finish times)
      }
   }

   // Step 2: Reverse the graph
   reverse_graph();

   // Step 3: Second DFS based on decreasing finishing times
   memset(visited, 0, sizeof(visited)); // Reset visited array

   printf("Strongly Connected Components:\n");
   for (int i = MAX_VERTICES - 1; i >= 0; i--) {
      int vertex = finishing_time[i];
      if (!visited[vertex]) {
         printf("SCC: ");
         DFS(vertex, 1); // Second pass DFS (print SCC)
         printf("\n");
      }
   }
}

int main() {
   find_scc();
   return 0;
}

Output

Following is the output of the above code:

Strongly Connected Components:
SCC: A C B
SCC: D E
#include <iostream>
#include <cstring>
using namespace std;

#define MAX_VERTICES 5

// Graph adjacency matrix
int adj[MAX_VERTICES][MAX_VERTICES] = {
   {0, 1, 0, 0, 0},
   {0, 0, 1, 0, 0},
   {1, 0, 0, 1, 0},
   {0, 0, 0, 0, 1},
   {0, 0, 0, 1, 0}
};

int visited[MAX_VERTICES] = {0};
int finishing_time[MAX_VERTICES];
int finish_index = 0;

// Mapping index to letters (A-E)
char map[MAX_VERTICES] = {'A', 'B', 'C', 'D', 'E'};

void DFS(int vertex, int print) {
   visited[vertex] = 1;
   if (print) cout << map[vertex] << " "; // Print vertex if it's in SCC traversal

   for (int v = 0; v < MAX_VERTICES; v++) {
      if (adj[vertex][v] && !visited[v]) {
         DFS(v, print);
      }
   }

   if (!print) finishing_time[finish_index++] = vertex; // Store finishing time if not printing SCC
}

void reverse_graph() {
   int temp[MAX_VERTICES][MAX_VERTICES];

   for (int i = 0; i < MAX_VERTICES; i++) {
      for (int j = 0; j < MAX_VERTICES; j++) {
         temp[i][j] = adj[i][j];
      }
   }

   // Transpose the graph
   for (int i = 0; i < MAX_VERTICES; i++) {
      for (int j = 0; j < MAX_VERTICES; j++) {
         adj[i][j] = temp[j][i];
      }
   }
}

void find_scc() {
   // Step 1: Compute finishing times in the original graph
   for (int v = 0; v < MAX_VERTICES; v++) {
      if (!visited[v]) {
         DFS(v, 0); // First pass DFS (store finish times)
      }
   }

   // Step 2: Reverse the graph
   reverse_graph();

   // Step 3: Second DFS based on decreasing finishing times
   memset(visited, 0, sizeof(visited)); // Reset visited array

   cout << "Strongly Connected Components:" << endl;
   for (int i = MAX_VERTICES - 1; i >= 0; i--) {
      int vertex = finishing_time[i];
      if (!visited[vertex]) {
         cout << "SCC: ";
         DFS(vertex, 1); // Second pass DFS (print SCC)
         cout << endl;
      }
   }
}

int main() {
   find_scc();
   return 0;
}

Output

Following is the output of the above code:

Strongly Connected Components:
SCC: A C B
SCC: D E
public class StronglyConnectedComponents {
   static final int MAX_VERTICES = 5;

   static int[][] adj = {
      {0, 1, 0, 0, 0},
      {0, 0, 1, 0, 0},
      {1, 0, 0, 1, 0},
      {0, 0, 0, 0, 1},
      {0, 0, 0, 1, 0}
   };

   static int[] visited = new int[MAX_VERTICES];
   static int[] finishing_time = new int[MAX_VERTICES];
   static int finish_index = 0;

   static char[] map = {'A', 'B', 'C', 'D', 'E'};

   static void DFS(int vertex, boolean recordFinishTime) {
      visited[vertex] = 1;

      if (!recordFinishTime) {
         System.out.print(map[vertex] + " ");
      }

      for (int v = 0; v < MAX_VERTICES; v++) {
         if (adj[vertex][v] == 1 && visited[v] == 0) {
            DFS(v, recordFinishTime);
         }
      }

      if (recordFinishTime) {
         finishing_time[finish_index++] = vertex;
      }
   }

   static void reverse_graph() {
      int[][] temp = new int[MAX_VERTICES][MAX_VERTICES];

      for (int i = 0; i < MAX_VERTICES; i++) {
         for (int j = 0; j < MAX_VERTICES; j++) {
            temp[i][j] = adj[j][i];
         }
      }

      adj = temp;
   }

   static void find_scc() {
      for (int v = 0; v < MAX_VERTICES; v++) {
         if (visited[v] == 0) {
            DFS(v, true);
         }
      }

      reverse_graph();
      visited = new int[MAX_VERTICES];

      System.out.println("Strongly Connected Components:");
      for (int i = MAX_VERTICES - 1; i >= 0; i--) {
         int vertex = finishing_time[i];
         if (visited[vertex] == 0) {
            System.out.print("SCC: ");
            DFS(vertex, false);
            System.out.println();
         }
      }
   }

   public static void main(String[] args) {
      find_scc();
   }
}

Output

Following is the output of the above code:

SCC: A C B
SCC: D E
MAX_VERTICES = 5

# Graph adjacency matrix
adj = [
   [0, 1, 0, 0, 0],
   [0, 0, 1, 0, 0],
   [1, 0, 0, 1, 0],
   [0, 0, 0, 0, 1],
   [0, 0, 0, 1, 0]
]

visited = [False] * MAX_VERTICES
finishing_time = [0] * MAX_VERTICES
finish_index = 0

# Mapping index to letters (A-E)
vertex_map = ['A', 'B', 'C', 'D', 'E']

def DFS(vertex, record_time):
   global finish_index  # Declare as global to avoid UnboundLocalError
   visited[vertex] = True
   
   if not record_time:
      print(vertex_map[vertex], end=" ")  # Print SCC members
   
   for v in range(MAX_VERTICES):
      if adj[vertex][v] and not visited[v]:
         DFS(v, record_time)
   
   if record_time:  # Store finishing order in first DFS pass
      finishing_time[finish_index] = vertex
      finish_index += 1
   
def reverse_graph():
    global adj
    adj = [[adj[j][i] for j in range(MAX_VERTICES)] for i in range(MAX_VERTICES)]

def find_scc():
   global visited, finish_index
   visited = [False] * MAX_VERTICES
   finish_index = 0

   # First DFS pass to get finishing times
   for v in range(MAX_VERTICES):
      if not visited[v]:
         DFS(v, True)

   # Step 2: Reverse the graph
   reverse_graph()

   # Step 3: Second DFS based on decreasing finishing times
   visited = [False] * MAX_VERTICES

   print("Strongly Connected Components:")
   for i in range(MAX_VERTICES - 1, -1, -1):
      vertex = finishing_time[i]
        if not visited[vertex]:
            print("SCC:", end=" ")
            DFS(vertex, False)
            print()

if __name__ == "__main__":
   find_scc()

Output

Following is the output of the above code:

SCC: A C B
SCC: D E

Tarjan's Algorithm

Tarjan's Algorithm is another popular algorithm to find strongly connected components in a directed graph. It uses Depth First Search (DFS) to traverse the graph and identify SCCs. Here's how it works:

  • Initialize a stack to keep track of visited nodes and their order of traversal.
  • Perform a DFS traversal of the graph, assigning a unique ID to each node and keeping track of the lowest ID reachable from that node.
  • Nodes with the same ID and low-link value form a strongly connected component.

Tarjan's Algorithm Example

Consider the same directed graph with nodes A, B, C, D, and E, and the following edges:

   A -> B
   B -> C
   C -> A
   C -> D
   D -> E
   E -> D

Using Tarjan's Algorithm, we can find the strongly connected components in the graph:

  • The strongly connected components are {A, B, C} and {D, E}.

Code for Tarjan's Algorithm

Here's an example code snippet for Tarjan's Algorithm in C, C++, Java, and Python:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAX_VERTICES 5

// Graph adjacency matrix
int adj[MAX_VERTICES][MAX_VERTICES] = {
   {0, 1, 0, 0, 0},
   {0, 0, 1, 0, 0},
   {1, 0, 0, 1, 0},
   {0, 0, 0, 0, 1},
   {0, 0, 0, 1, 0}
};

int visited[MAX_VERTICES] = {0};
int stack[MAX_VERTICES];
int stack_index = 0;
int ids[MAX_VERTICES];
int low[MAX_VERTICES];
int id = 0;

// Mapping index to letters (A-E)
char map[MAX_VERTICES] = {'A', 'B', 'C', 'D', 'E'};
int map_index = 0;

void DFS(int vertex) {
   stack[stack_index++] = vertex;
   visited[vertex] = 1;
   ids[vertex] = low[vertex] = id++;

   for (int v = 0; v < MAX_VERTICES; v++) {
      if (adj[vertex][v]) {
         if (!visited[v]) {
            DFS(v);
         }
         if (ids[v] != -1) {
            low[vertex] = low[vertex]  0) {
         int node = stack[--stack_index];
         low[node] = ids[vertex];
         printf("%c ", map[node]);
         if (node == vertex) break;
      }
      printf("\n");
   }
}

void find_scc() {
   memset(ids, -1, sizeof(ids));
   memset(low, -1, sizeof(low));

   for (int v = 0; v < MAX_VERTICES; v++) {
      if (!visited[v]) {
         DFS(v);
      }
   }
}

int main() {
   find_scc();
   return 0;
}

Output

Following is the output of the above code:

SCC: E D
SCC: C B A
#include <iostream>
#include <cstring>
using namespace std;

#define MAX_VERTICES 5

// Graph adjacency matrix
int adj[MAX_VERTICES][MAX_VERTICES] = {
   {0, 1, 0, 0, 0},
   {0, 0, 1, 0, 0},
   {1, 0, 0, 1, 0},
   {0, 0, 0, 0, 1},
   {0, 0, 0, 1, 0}
};

int visited[MAX_VERTICES] = {0};
int stack[MAX_VERTICES];
int stack_index = 0;
int ids[MAX_VERTICES];
int low[MAX_VERTICES];
int id = 0;

// Mapping index to letters (A-E)
char map[MAX_VERTICES] = {'A', 'B', 'C', 'D', 'E'};
int map_index = 0;

void DFS(int vertex) {
   stack[stack_index++] = vertex;
   visited[vertex] = 1;
   ids[vertex] = low[vertex] = id++;

   for (int v = 0; v < MAX_VERTICES; v++) {
      if (adj[vertex][v]) {
         if (!visited[v]) {
            DFS(v);
         }
         if (ids[v] != -1) {
            low[vertex] = low[vertex] < low[v] ? low[vertex] : low[v];
         }
      }
   }

   if (ids[vertex] == low[vertex]) {
      cout << "SCC: ";
      while (stack_index > 0) {
         int node = stack[--stack_index];
         low[node] = ids[vertex];
         cout << map[node] << " ";
         if (node == vertex) break;
      }
      cout << endl;
   }
}

void find_scc() {
   memset(ids, -1, sizeof(ids));
   memset(low, -1, sizeof(low));

   for (int v = 0; v < MAX_VERTICES; v++) {
      if (!visited[v]) {
         DFS(v);
      }
   }
}

int main() {
   find_scc();
   return 0;
}

Output

Following is the output of the above code:

SCC: E D
SCC: C B A
public class TarjansAlgorithm {
   static final int MAX_VERTICES = 5;

   // Graph adjacency matrix
   static int[][] adj = {
      {0, 1, 0, 0, 0},
      {0, 0, 1, 0, 0},
      {1, 0, 0, 1, 0},
      {0, 0, 0, 0, 1},
      {0, 0, 0, 1, 0}
   };

   static int[] visited = new int[MAX_VERTICES];
   static int[] stack = new int[MAX_VERTICES];
   static int stack_index = 0;
   static int[] ids = new int[MAX_VERTICES];
   static int[] low = new int[MAX_VERTICES];
   static int id = 0;

   // Mapping index to letters (A-E)
   static char[] map = {'A', 'B', 'C', 'D', 'E'};
   static int map_index = 0;

   static void DFS(int vertex) {
      stack[stack_index++] = vertex;
      visited[vertex] = 1;
      ids[vertex] = low[vertex] = id++;

      for (int v = 0; v < MAX_VERTICES; v++) {
         if (adj[vertex][v] == 1) {
            if (visited[v] == 0) {
               DFS(v);
            }
            if (ids[v] != -1) {
               low[vertex] = low[vertex] < low[v] ? low[vertex] : low[v];
            }
         }
      }

      if (ids[vertex] == low[vertex]) {
         System.out.print("SCC: ");
         while (stack_index > 0) {
            int node = stack[--stack_index];
            low[node] = ids[vertex];
            System.out.print(map[node] + " ");
            if (node == vertex) break;
         }
         System.out.println();
      }
   }

   static void find_scc() {
      for (int v = 0; v < MAX_VERTICES; v++) {
         if (visited[v] == 0) {
            DFS(v);
         }
      }
   }

   public static void main(String[] args) {
      find_scc();
   }
}

Output

Following is the output of the above code:

SCC: E D
SCC: C B A
MAX_VERTICES = 5

adj = [
   [0, 1, 0, 0, 0],
   [0, 0, 1, 0, 0],
   [1, 0, 0, 1, 0],
   [0, 0, 0, 0, 1],
   [0, 0, 0, 1, 0]
]

visited = [False] * MAX_VERTICES
stack = []
on_stack = [False] * MAX_VERTICES
ids = [-1] * MAX_VERTICES
low = [-1] * MAX_VERTICES
scc_count = 0
id = 0

map_index = ['A', 'B', 'C', 'D', 'E']

def DFS(vertex):
   global id, scc_count
   stack.append(vertex)
   on_stack[vertex] = True
   ids[vertex] = low[vertex] = id
   id += 1
   
   for v in range(MAX_VERTICES):
       if adj[vertex][v] == 1:
           if ids[v] == -1:  # Unvisited
               DFS(v)
               low[vertex] = min(low[vertex], low[v])
           elif on_stack[v]:  # In the current SCC
               low[vertex] = min(low[vertex], ids[v])
   
   if ids[vertex] == low[vertex]:
       print("SCC:", end=" ")
       while True:
           node = stack.pop()
           on_stack[node] = False
           low[node] = ids[vertex]
           print(map_index[node], end=" ")
           if node == vertex:
               break
       print()
       scc_count += 1
   
def find_scc():
    for v in range(MAX_VERTICES):
        if ids[v] == -1:
            DFS(v)

find_scc()

Output

Following is the output of the above code:

SCC: E D
SCC: C B A

Applications of Strongly Connected Components

Following are some common applications of Strongly Connected Components:

  • Network Analysis: SCCs help in understanding the structure and connectivity of networks, such as social networks, transportation networks, and communication networks.
  • Graph Algorithms: SCCs are used in various graph algorithms, such as cycle detection, pathfinding, and network flow optimization.
  • Software Engineering: SCCs are useful in software engineering for dependency analysis, module identification, and code refactoring.

Conclusion

Understanding Strongly Connected Components is essential for analyzing the connectivity and structure of directed graphs. Algorithms like Kosaraju's Algorithm and Tarjan's Algorithm provide efficient ways to identify SCCs in graphs, enabling various applications in network analysis, graph algorithms, and software engineering.

Advertisements