def SCC_Tarjan(self,n=None,stack=None,low_link=None,scc=None,visited=None):
if visited is None:
visited = [self.get_n()[0]]
if scc is None:
scc = [[]]
if low_link is None:
low_link = [i for i in range(len(self.nodes))]
if n is None:
n = [self.get_n()[0],None,0,0] # pos 0: the node / pos 1: the parent / pos 2: low link / pos 3: index
if stack is None:
stack = [n[0]]
def get_low_link(x):
try:
return low_link[stack.index(x)]
except:
return inf
def get_id(x):
return stack.index(x)
print(f'NOW IN {n[0]}')
for i in self.node_out_neighbours(n[0]):
if i not in stack and i not in visited:
stack.append(i)
return self.SCC_Tarjan(n=[i,n,get_low_link(i),get_id(i)],stack=stack,low_link=low_link,scc=scc,visited=visited)
elif get_low_link(i) < n[2] and i in stack and self.is_an_arc(n[0],i):# i != n[1][0] and i in stack:
print(f'UPDATED {n[0]} TO {i} S LOW LINK')
low_link[stack.index(n[0])] = get_low_link(i)
n[2] = get_low_link(i)
else:
if n[2] != n[3]:
n[1][2] = min(n[2],n[1][2])
print(f"{n[1][0]} S LOW LINK GOT UPDATED TO {n[0]} S LOW LINK WHICH IS {n[2]}")
visited.append(n[0])
scc[-1].append(n[0])
print(f"BACK TRACK FROM {n[0]}")
return self.SCC_Tarjan(n=n[1],stack=stack,low_link=low_link,scc=scc,visited=list(set(visited)))
else:
print(f"{n[0]} IS THE START OF THE SCC")
visited.append(n[0])
scc[-1].append(n[0])
scc.append([])
if stack.index(n[0]) != 0:
stack = stack[:stack.index(n[0])]
n1 = n[1]
return self.SCC_Tarjan(n=n1, scc=scc, visited=list(set(visited)), stack=stack, low_link=low_link)
else:
if len(self.nodes) == len(visited):
return scc[:-1]
new_start = list(set(self.nodes).difference(set(visited)))[0]
n1 = [new_start,None,0,0]
stack = None
return self.SCC_Tarjan(n=n1,scc=scc, visited=list(set(visited)),stack=stack)
ok, this is my tarjan algorithm implementation in python. i know that it is messy, that s why i would like that you could suggest some alterations to enhance its efficiency. since i am aware that it is not very readable, i will explain every peculiarity in this code:
first, the function has some positional arguments that are set to none in order to escape from the “mutable argument warning”, but in the beginning of the function i kinda fixed it.
the visited variable contains the nodes that are already in an SCC.
scc is a list of lists, where each element contains the nodes of an SCC in no particular order
the n variable is a list containing our node, its parent(note that even the parent is in this list format except for the beginning nodes, so you might think of n as a nested list), its low link value and its id.
the last variable is low link which is not as you may think of it. why? i will explain that later(the trick is in the last line where after the end of an SCC,i will reinitialize it as in the beginning).
NOTE: self.get_n() is a function that returns the graph s nodes
i then defined a function get_low_link(). the reason i used a try/except structure is that if you head to the elif statement in the for loop, you notice that i used this function even though sometimes the var i can be a node that is not in the stack.
NOTE: ignore the print statements, i just used them to check the dfs part
next, the for loop:i will check the out neighbours of a node, if it s not in the stack and of course not visited, i will use recursion on this new node. elif getlowlink(2) < n[2] is meant to check if i is not in visited, remember, otherwise the getlowlink func will return inf. the is_an_arc part is simple to understand.
Now lets head to the else part. the first if statement checks if the node is the start of the scc(id == low link) and then we will back track to the predecessor node until we fall in the next else statement, meaning that our node is the start of a scc. what i did is that i checked if the whole stack is an SCC, in the contrary case, we will continue our algo with the predecessor node (top of the stack) PRESERVING THE LOW_LINK LIST. if the stack will be empty, first we check whether we visited all the nodes, in that case, terminating the algorithm, otherwise, we define new_start to be the next start node and when we use recursion this time, THE LOW LINK LIST IS NOW LIKE IN THE BEGINNING, THAT IS WE WILL BEGIN BY ID 0,1… AND NOT CONTINUE WITH OUR LOW LINKS AND ID.
this is it, i hope i didnt cause a lot of confusion. thanks in advance
ProgrammingLover is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
2