Last active
May 16, 2023 11:08
-
-
Save mohamed/4a5f5931d11bc2e68081bf83c5c0883e to your computer and use it in GitHub Desktop.
Tarjan's strongly connected components algorithm
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
Tarjan's strongly connected components algorithm + Topological sort | |
Taken from: http://www.logarithmic.net/pfh/blog/01208083168 | |
Public domain. Use it as you will | |
""" | |
def strongly_connected_components(graph): | |
""" | |
Tarjan's Algorithm (named for its discoverer, Robert Tarjan) is a graph | |
theory algorithm for finding the strongly connected components of a graph. | |
Based on: | |
http://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm | |
""" | |
index_counter = [0] | |
stack = [] | |
lowlinks = {} | |
index = {} | |
result = [] | |
def strongconnect(node): | |
# set the depth index for this node to the smallest unused index | |
index[node] = index_counter[0] | |
lowlinks[node] = index_counter[0] | |
index_counter[0] += 1 | |
stack.append(node) | |
# Consider successors of `node` | |
try: | |
successors = graph[node] | |
except KeyError: | |
successors = [] | |
for successor in successors: | |
if successor not in lowlinks: | |
# Successor has not yet been visited; recurse on it | |
strongconnect(successor) | |
lowlinks[node] = min(lowlinks[node], lowlinks[successor]) | |
elif successor in stack: | |
# the successor is in the stack and hence in the current | |
# strongly connected component (SCC) | |
lowlinks[node] = min(lowlinks[node], index[successor]) | |
# If `node` is a root node, pop the stack and generate an SCC | |
if lowlinks[node] == index[node]: | |
connected_component = [] | |
while True: | |
successor = stack.pop() | |
connected_component.append(successor) | |
if successor == node: | |
break | |
component = tuple(connected_component) | |
# storing the result | |
result.append(component) | |
for node in graph: | |
if node not in lowlinks: | |
strongconnect(node) | |
return result | |
def topological_sort(graph): | |
""" | |
Performs topological sort on the graph | |
""" | |
count = {} | |
for node in graph: | |
count[node] = 0 | |
for node in graph: | |
for successor in graph[node]: | |
count[successor] += 1 | |
ready = [node for node in graph if count[node] == 0] | |
result = [] | |
while ready: | |
node = ready.pop(-1) | |
result.append(node) | |
for successor in graph[node]: | |
count[successor] -= 1 | |
if count[successor] == 0: | |
ready.append(successor) | |
return result | |
def robust_topological_sort(graph): | |
""" First identify strongly connected components, | |
then perform a topological sort on these components. """ | |
components = strongly_connected_components(graph) | |
node_component = {} | |
for component in components: | |
for node in component: | |
node_component[node] = component | |
component_graph = {} | |
for component in components: | |
component_graph[component] = [] | |
for node in graph: | |
node_c = node_component[node] | |
for successor in graph[node]: | |
successor_c = node_component[successor] | |
if node_c != successor_c: | |
component_graph[node_c].append(successor_c) | |
return topological_sort(component_graph) | |
def is_acyclic(graph): | |
""" | |
Checks if the specified graph is acyclic or not. | |
Returns True if acyclic, False otherwise | |
""" | |
components = strongly_connected_components(graph) | |
for comp in components: | |
if len(comp) > 1: | |
return False | |
return True | |
if __name__ == '__main__': | |
g1 = { | |
0: [1], | |
1: [2], | |
2: [1, 3], | |
3: [3], | |
} | |
g2 = { | |
0: [1], | |
1: [2], | |
2: [3], | |
3: [], | |
} | |
graphs = [g1, g2] | |
for g in graphs: | |
print(robust_topological_sort(g)) | |
print(f"Is the graph acyclic? {is_acyclic(g)}") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment