From a7747fe1335c9053b9dd7f9e15736c06f2f882d3 Mon Sep 17 00:00:00 2001 From: Param Parekh <129577837+Param1304@users.noreply.github.com> Date: Sun, 27 Oct 2024 16:39:58 +0530 Subject: [PATCH] Create gabows_algorithm.py Implemented Gabow's algorithm to find all the strongly connected components in a directed graph with a time complexity of O(V+E) as mentioned in the issue #12297 --- graphs/gabows_algorithm.py | 115 +++++++++++++++++++++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 graphs/gabows_algorithm.py diff --git a/graphs/gabows_algorithm.py b/graphs/gabows_algorithm.py new file mode 100644 index 000000000000..fe5d059d2e40 --- /dev/null +++ b/graphs/gabows_algorithm.py @@ -0,0 +1,115 @@ +""" +This is a pure Python implementation +of Gabow's algorithm for finding +strongly connected components (SCCs) +in a directed graph. +For doctests run: + python -m doctest -v gabow_algorithm.py +or + python3 -m doctest -v gabow_algorithm.py +For manual testing run: + python gabow_algorithm.py +""" +from collections import defaultdict +from typing import List, Dict +from __future__ import annotations +class Graph: + """ + Graph data structure to represent + a directed graph and find SCCs + using Gabow's algorithm. + + Attributes: + vertices (int): Number of + vertices in the graph. + graph (Dict[int, List[int]]): + Adjacency list of the graph. + + Methods: + add_edge(u, v): Adds an edge + from vertex u to vertex v. + find_sccs(): Finds and returns + all SCCs in the graph. + + Examples: + >>> g = Graph(5) + >>> g.add_edge(0, 2) + >>> g.add_edge(2, 1) + >>> g.add_edge(1, 0) + >>> g.add_edge(0, 3) + >>> g.add_edge(3, 4) + >>> sorted(g.find_sccs()) + [[0, 1, 2], [3], [4]] + """ + def __init__(self, vertices: int) -> None: + self.vertices = vertices + self.graph: Dict[int, List[int]] = defaultdict(list) + self.index = 0 + self.stack_s = [] # Stack S + self.stack_p = [] # Stack P + self.visited = [False] * vertices + self.result = [] + + def add_edge(self, u: int, v: int) -> None: + """ + Adds a directed edge from vertex u to vertex v. + + :param u: Starting vertex of the edge. + :param v: Ending vertex of the edge. + """ + self.graph[u].append(v) + def _dfs(self, v: int) -> None: + """ + Depth-first search helper function to + process each vertex and identify SCCs. + + :param v: The current vertex to process in DFS. + """ + self.visited[v] = True + self.stack_s.append(v) + self.stack_p.append(v) + + for neighbor in self.graph[v]: + if not self.visited[neighbor]: + self._dfs(neighbor) + elif neighbor in self.stack_p: + while self.stack_p and self.stack_p[-1] != neighbor: + self.stack_p.pop() + + if self.stack_p and self.stack_p[-1] == v: + scc = [] + while True: + node = self.stack_s.pop() + scc.append(node) + if node == v: + break + self.stack_p.pop() + self.result.append(scc) + + def find_sccs(self) -> List[List[int]]: + """ + Finds all strongly connected components + in the directed graph. + :return: List of SCCs, where each SCC is + represented as a list of vertices. + """ + for v in range(self.vertices): + if not self.visited[v]: + self._dfs(v) + return self.result +if __name__ == "__main__": + import doctest + doctest.testmod() + # Example usage for manual testing + try: + vertex_count = int(input("Enter the number of vertices: ")) + g = Graph(vertex_count) + edge_count = int(input("Enter the number of edges: ")) + print("Enter each edge as a pair of vertices (u v):") + for _ in range(edge_count): + u, v = map(int, input().split()) + g.add_edge(u, v) + sccs = g.find_sccs() + print("Strongly Connected Components:", sccs) + except ValueError: + print("Invalid input. Please enter valid integers.")