From f3fe173bb72fc6bd487b43c80aa804840f651f85 Mon Sep 17 00:00:00 2001 From: nikalosa Date: Sun, 14 Jun 2020 15:37:58 +0400 Subject: [PATCH 1/9] Implement strongly connected components for graph algorithms --- graphs/strongly_connected_components.py | 103 ++++++++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 graphs/strongly_connected_components.py diff --git a/graphs/strongly_connected_components.py b/graphs/strongly_connected_components.py new file mode 100644 index 000000000000..7697bc4c99aa --- /dev/null +++ b/graphs/strongly_connected_components.py @@ -0,0 +1,103 @@ +''' +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, vert, visited, order): + """ + Use depth first search to sort graph + At this time graph is the same as input + """ + + visited[vert] = True + + for neighbour in graph[vert]: + if not visited[neighbour]: + topology_sort(graph, neighbour, visited, order) + + order.append(vert) + + +def find_components(reverse_graph, vert, visited, component): + """ + Use depth first search to find strongliy connected + vertices. Now graph is reversed + """ + + visited[vert] = True + + component.append(vert) + + for neighbour in reverse_graph[vert]: + if not visited[neighbour]: + topology_sort(reverse_graph, neighbour, visited, 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, 2, 1], [3], [4]] + >>> strongly_connected_components(test_graph_2) + [[0, 1, 2], [3, 4, 5]] + """ + + n = len(graph) + + visited = n * [False] + + # describe reversed graph (All edges reversed) + reverse_graph = {vert: [] for vert in range(n)} + + for vert, neighbours in graph.items(): + for neighbour in neighbours: + reverse_graph[neighbour].append(vert) + + order = [] + + # topology sort + for i in range(n): + if not visited[i]: + topology_sort(graph, i, visited, order) + + # answer list + components_list = [] + # reinitialize visited list + visited = n * [False] + + # finding stongly connected components + for i in range(n): + vert = order[n - i - 1] + if not visited[vert]: + component = [] + find_components(reverse_graph, vert, visited, component) + components_list.append(component) + + return components_list + + +if __name__ == "__main__": + + import doctest + doctest.testmod() From 96cab027b4044dbb179939e612cc502fb6cbddc0 Mon Sep 17 00:00:00 2001 From: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Date: Sun, 14 Jun 2020 11:39:02 +0000 Subject: [PATCH 2/9] fixup! Format Python code with psf/black push --- graphs/strongly_connected_components.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/graphs/strongly_connected_components.py b/graphs/strongly_connected_components.py index 7697bc4c99aa..c44fa706e7da 100644 --- a/graphs/strongly_connected_components.py +++ b/graphs/strongly_connected_components.py @@ -1,9 +1,9 @@ -''' +""" https://en.wikipedia.org/wiki/Strongly_connected_component Finding strongly connected components in directed graph -''' +""" test_graph_1 = { 0: [2, 3], @@ -100,4 +100,5 @@ def strongly_connected_components(graph: dict) -> list: if __name__ == "__main__": import doctest + doctest.testmod() From c682fcc7df9f4bc089d966ac6cf18cce51f5e628 Mon Sep 17 00:00:00 2001 From: nikalosa Date: Sun, 14 Jun 2020 15:54:35 +0400 Subject: [PATCH 3/9] Delete trailing whitespace --- graphs/strongly_connected_components.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/graphs/strongly_connected_components.py b/graphs/strongly_connected_components.py index 7697bc4c99aa..be555f4a3fe3 100644 --- a/graphs/strongly_connected_components.py +++ b/graphs/strongly_connected_components.py @@ -41,7 +41,7 @@ def topology_sort(graph, vert, visited, order): def find_components(reverse_graph, vert, visited, component): """ Use depth first search to find strongliy connected - vertices. Now graph is reversed + vertices. Now graph is reversed """ visited[vert] = True From c4a3b3aed8f3e51ec47c28025312485eb260af81 Mon Sep 17 00:00:00 2001 From: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Date: Sun, 14 Jun 2020 11:55:58 +0000 Subject: [PATCH 4/9] updating DIRECTORY.md --- DIRECTORY.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/DIRECTORY.md b/DIRECTORY.md index c90043aa734f..53cdd7782818 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -257,6 +257,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 @@ -264,6 +265,7 @@ * [Test Knapsack](https://github.com/TheAlgorithms/Python/blob/master/greedy_method/test_knapsack.py) ## Hashes + * [Adler32](https://github.com/TheAlgorithms/Python/blob/master/hashes/adler32.py) * [Chaos Machine](https://github.com/TheAlgorithms/Python/blob/master/hashes/chaos_machine.py) * [Enigma Machine](https://github.com/TheAlgorithms/Python/blob/master/hashes/enigma_machine.py) * [Hamming Code](https://github.com/TheAlgorithms/Python/blob/master/hashes/hamming_code.py) From 8d57fee3635f5f66180c72fd7776b876ec2b5545 Mon Sep 17 00:00:00 2001 From: nikalosa Date: Mon, 15 Jun 2020 18:39:32 +0400 Subject: [PATCH 5/9] Add doctests and typehints --- graphs/strongly_connected_components.py | 33 +++++++++++++++++-------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/graphs/strongly_connected_components.py b/graphs/strongly_connected_components.py index 1b324b8044cd..2a01e546f0b3 100644 --- a/graphs/strongly_connected_components.py +++ b/graphs/strongly_connected_components.py @@ -23,34 +23,47 @@ } -def topology_sort(graph, vert, visited, order): +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]: - topology_sort(graph, neighbour, visited, order) + order += topology_sort(graph, neighbour, visited) order.append(vert) + return order + -def find_components(reverse_graph, vert, visited, component): +def find_components(reverse_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.append(vert) + component = [vert] for neighbour in reverse_graph[vert]: if not visited[neighbour]: - topology_sort(reverse_graph, neighbour, visited, component) + component += find_components(reverse_graph, neighbour, visited) + + return component def strongly_connected_components(graph: dict) -> list: @@ -58,9 +71,9 @@ 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, 2, 1], [3], [4]] + [[0, 1, 2], [3], [4]] >>> strongly_connected_components(test_graph_2) - [[0, 1, 2], [3, 4, 5]] + [[0, 2, 1], [3, 5, 4]] """ n = len(graph) @@ -79,7 +92,8 @@ def strongly_connected_components(graph: dict) -> list: # topology sort for i in range(n): if not visited[i]: - topology_sort(graph, i, visited, order) + order += topology_sort(graph, i, visited) + # print(order, 'aeee\n') # answer list components_list = [] @@ -90,8 +104,7 @@ def strongly_connected_components(graph: dict) -> list: for i in range(n): vert = order[n - i - 1] if not visited[vert]: - component = [] - find_components(reverse_graph, vert, visited, component) + component = find_components(reverse_graph, vert, visited) components_list.append(component) return components_list From 1995b9a2cc119fd6b1ef704f3032227032713188 Mon Sep 17 00:00:00 2001 From: Nika Losaberidze Date: Wed, 17 Jun 2020 17:05:43 +0400 Subject: [PATCH 6/9] Remove unnecessary comments, change variable names --- graphs/strongly_connected_components.py | 37 ++++++++----------------- 1 file changed, 12 insertions(+), 25 deletions(-) diff --git a/graphs/strongly_connected_components.py b/graphs/strongly_connected_components.py index 2a01e546f0b3..1d7ad499849b 100644 --- a/graphs/strongly_connected_components.py +++ b/graphs/strongly_connected_components.py @@ -45,7 +45,7 @@ def topology_sort(graph: dict, vert: int, visited: list) -> list: return order -def find_components(reverse_graph: dict, vert: int, visited: list) -> list: +def find_components(reversed_graph: dict, vert: int, visited: list) -> list: """ Use depth first search to find strongliy connected vertices. Now graph is reversed @@ -56,12 +56,11 @@ def find_components(reverse_graph: dict, vert: int, visited: list) -> list: """ visited[vert] = True - component = [vert] - for neighbour in reverse_graph[vert]: + for neighbour in reversed_graph[vert]: if not visited[neighbour]: - component += find_components(reverse_graph, neighbour, visited) + component += find_components(reversed_graph, neighbour, visited) return component @@ -75,43 +74,31 @@ def strongly_connected_components(graph: dict) -> list: >>> strongly_connected_components(test_graph_2) [[0, 2, 1], [3, 5, 4]] """ - - n = len(graph) - - visited = n * [False] - - # describe reversed graph (All edges reversed) - reverse_graph = {vert: [] for vert in range(n)} + + visited = len(graph) * [False] + reversed_graph = {vert: [] for vert in range(n)} for vert, neighbours in graph.items(): for neighbour in neighbours: - reverse_graph[neighbour].append(vert) + reversed_graph[neighbour].append(vert) order = [] - - # topology sort - for i in range(n): + for i in range(len(graph)): if not visited[i]: order += topology_sort(graph, i, visited) - # print(order, 'aeee\n') - # answer list components_list = [] - # reinitialize visited list - visited = n * [False] + visited = len(graph) * [False] - # finding stongly connected components - for i in range(n): - vert = order[n - i - 1] + for i in range(len(graph)): + vert = order[len(graph) - i - 1] if not visited[vert]: - component = find_components(reverse_graph, vert, visited) + component = find_components(reversed_graph, vert, visited) components_list.append(component) return components_list if __name__ == "__main__": - import doctest - doctest.testmod() From dfaf1af583504d68ac995e8340dbbe3ecb31c901 Mon Sep 17 00:00:00 2001 From: github-actions <${GITHUB_ACTOR}@users.noreply.github.com> Date: Wed, 17 Jun 2020 13:06:15 +0000 Subject: [PATCH 7/9] fixup! Format Python code with psf/black push --- graphs/strongly_connected_components.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/graphs/strongly_connected_components.py b/graphs/strongly_connected_components.py index 1d7ad499849b..cb297121d0e9 100644 --- a/graphs/strongly_connected_components.py +++ b/graphs/strongly_connected_components.py @@ -74,7 +74,7 @@ def strongly_connected_components(graph: dict) -> list: >>> strongly_connected_components(test_graph_2) [[0, 2, 1], [3, 5, 4]] """ - + visited = len(graph) * [False] reversed_graph = {vert: [] for vert in range(n)} @@ -101,4 +101,5 @@ def strongly_connected_components(graph: dict) -> list: if __name__ == "__main__": import doctest + doctest.testmod() From 1a8f04ba803f2a5d36d8b7e7b8f68982b6bc171e Mon Sep 17 00:00:00 2001 From: Nika Losaberidze Date: Wed, 17 Jun 2020 17:12:23 +0400 Subject: [PATCH 8/9] Change undefined variable's name --- graphs/strongly_connected_components.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/graphs/strongly_connected_components.py b/graphs/strongly_connected_components.py index cb297121d0e9..b0c0a516a80a 100644 --- a/graphs/strongly_connected_components.py +++ b/graphs/strongly_connected_components.py @@ -76,7 +76,7 @@ def strongly_connected_components(graph: dict) -> list: """ visited = len(graph) * [False] - reversed_graph = {vert: [] for vert in range(n)} + reversed_graph = {vert: [] for vert in range(len(graph))} for vert, neighbours in graph.items(): for neighbour in neighbours: From 298800ace9c661053cb91ad4430a08a1595bdbc3 Mon Sep 17 00:00:00 2001 From: Nika Losaberidze Date: Wed, 17 Jun 2020 18:28:54 +0400 Subject: [PATCH 9/9] Apply suggestions from code review Co-authored-by: Christian Clauss --- graphs/strongly_connected_components.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/graphs/strongly_connected_components.py b/graphs/strongly_connected_components.py index b0c0a516a80a..283545c9a618 100644 --- a/graphs/strongly_connected_components.py +++ b/graphs/strongly_connected_components.py @@ -83,8 +83,8 @@ def strongly_connected_components(graph: dict) -> list: reversed_graph[neighbour].append(vert) order = [] - for i in range(len(graph)): - if not visited[i]: + for i, was_visited in enumerate(visited): + if not was_visited: order += topology_sort(graph, i, visited) components_list = []