From fb3a228d2695d083208ecac26ff1e7b5a9f463b0 Mon Sep 17 00:00:00 2001 From: Nika Losaberidze Date: Wed, 17 Jun 2020 20:16:54 +0400 Subject: [PATCH] Strongly connected components (#2114) * Implement strongly connected components for graph algorithms * fixup! Format Python code with psf/black push * Delete trailing whitespace * updating DIRECTORY.md * Add doctests and typehints * Remove unnecessary comments, change variable names * fixup! Format Python code with psf/black push * Change undefined variable's name * Apply suggestions from code review Co-authored-by: Christian Clauss Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Co-authored-by: Christian Clauss --- DIRECTORY.md | 1 + graphs/strongly_connected_components.py | 105 ++++++++++++++++++++++++ 2 files changed, 106 insertions(+) create mode 100644 graphs/strongly_connected_components.py diff --git a/DIRECTORY.md b/DIRECTORY.md index 9ab6a457e..01f421613 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -261,6 +261,7 @@ * [Page Rank](https://github.com/TheAlgorithms/Python/blob/master/graphs/page_rank.py) * [Prim](https://github.com/TheAlgorithms/Python/blob/master/graphs/prim.py) * [Scc Kosaraju](https://github.com/TheAlgorithms/Python/blob/master/graphs/scc_kosaraju.py) + * [Strongly Connected Components](https://github.com/TheAlgorithms/Python/blob/master/graphs/strongly_connected_components.py) * [Tarjans Scc](https://github.com/TheAlgorithms/Python/blob/master/graphs/tarjans_scc.py) ## Greedy Method diff --git a/graphs/strongly_connected_components.py b/graphs/strongly_connected_components.py new file mode 100644 index 000000000..283545c9a --- /dev/null +++ b/graphs/strongly_connected_components.py @@ -0,0 +1,105 @@ +""" +https://en.wikipedia.org/wiki/Strongly_connected_component + +Finding strongly connected components in directed graph + +""" + +test_graph_1 = { + 0: [2, 3], + 1: [0], + 2: [1], + 3: [4], + 4: [], +} + +test_graph_2 = { + 0: [1, 2, 3], + 1: [2], + 2: [0], + 3: [4], + 4: [5], + 5: [3], +} + + +def topology_sort(graph: dict, vert: int, visited: list) -> list: + """ + Use depth first search to sort graph + At this time graph is the same as input + >>> topology_sort(test_graph_1, 0, 5 * [False]) + [1, 2, 4, 3, 0] + >>> topology_sort(test_graph_2, 0, 6 * [False]) + [2, 1, 5, 4, 3, 0] + """ + + visited[vert] = True + order = [] + + for neighbour in graph[vert]: + if not visited[neighbour]: + order += topology_sort(graph, neighbour, visited) + + order.append(vert) + + return order + + +def find_components(reversed_graph: dict, vert: int, visited: list) -> list: + """ + Use depth first search to find strongliy connected + vertices. Now graph is reversed + >>> find_components({0: [1], 1: [2], 2: [0]}, 0, 5 * [False]) + [0, 1, 2] + >>> find_components({0: [2], 1: [0], 2: [0, 1]}, 0, 6 * [False]) + [0, 2, 1] + """ + + visited[vert] = True + component = [vert] + + for neighbour in reversed_graph[vert]: + if not visited[neighbour]: + component += find_components(reversed_graph, neighbour, visited) + + return component + + +def strongly_connected_components(graph: dict) -> list: + """ + This function takes graph as a parameter + and then returns the list of strongly connected components + >>> strongly_connected_components(test_graph_1) + [[0, 1, 2], [3], [4]] + >>> strongly_connected_components(test_graph_2) + [[0, 2, 1], [3, 5, 4]] + """ + + visited = len(graph) * [False] + reversed_graph = {vert: [] for vert in range(len(graph))} + + for vert, neighbours in graph.items(): + for neighbour in neighbours: + reversed_graph[neighbour].append(vert) + + order = [] + for i, was_visited in enumerate(visited): + if not was_visited: + order += topology_sort(graph, i, visited) + + components_list = [] + visited = len(graph) * [False] + + for i in range(len(graph)): + vert = order[len(graph) - i - 1] + if not visited[vert]: + component = find_components(reversed_graph, vert, visited) + components_list.append(component) + + return components_list + + +if __name__ == "__main__": + import doctest + + doctest.testmod()