From 95412ada5ba6c84bf02209d14e2c210efeb4082d Mon Sep 17 00:00:00 2001 From: Param Parekh <129577837+Param1304@users.noreply.github.com> Date: Sun, 27 Oct 2024 17:52:41 +0530 Subject: [PATCH 1/2] 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 | 113 +++++++++++++++++++++++++++++++++++++ 1 file changed, 113 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..bbf3731a8178 --- /dev/null +++ b/graphs/gabows_algorithm.py @@ -0,0 +1,113 @@ +""" +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 +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.") From 86a5149cb0693a71b06a414659da7ef06953a66b Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 27 Oct 2024 12:24:12 +0000 Subject: [PATCH 2/2] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- graphs/gabows_algorithm.py | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/graphs/gabows_algorithm.py b/graphs/gabows_algorithm.py index bbf3731a8178..2e6f6132aaac 100644 --- a/graphs/gabows_algorithm.py +++ b/graphs/gabows_algorithm.py @@ -1,7 +1,7 @@ """ -This is a pure Python implementation of +This is a pure Python implementation of Gabow's algorithm for finding -strongly connected components (SCCs) +strongly connected components (SCCs) in a directed graph. For doctests run: @@ -14,22 +14,24 @@ from collections import defaultdict from typing import List, Dict + + class Graph: """ - Graph data structure to represent + Graph data structure to represent a directed graph and find SCCs using Gabow's algorithm. Attributes: - vertices (int): Number of + vertices (int): Number of vertices in the graph. - graph (Dict[int, List[int]]): + graph (Dict[int, List[int]]): Adjacency list of the graph. Methods: - add_edge(u, v): Adds an edge + add_edge(u, v): Adds an edge from vertex u to vertex v. - find_sccs(): Finds and returns + find_sccs(): Finds and returns all SCCs in the graph. Examples: @@ -42,6 +44,7 @@ class Graph: >>> 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) @@ -50,6 +53,7 @@ def __init__(self, vertices: int) -> None: 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. @@ -61,7 +65,7 @@ def add_edge(self, u: int, v: int) -> None: def _dfs(self, v: int) -> None: """ - Depth-first search helper function to + Depth-first search helper function to process each vertex and identify SCCs. :param v: The current vertex to process in DFS. """ @@ -84,19 +88,23 @@ def _dfs(self, v: int) -> None: break self.stack_p.pop() self.result.append(scc) + def find_sccs(self) -> List[List[int]]: """ - Finds all strongly connected components + Finds all strongly connected components in the directed graph. - :return: List of SCCs, where each SCC + :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: