def bfs(self, node: Node, out: bool) -> list: """ BFS algorithm Helper algorithm for the SCC algorithm. :param node: - a node to start BFS searching from. :param out: - determines how to search: out - search all out edges of node, in - search al in edges of node. :return: - a list representing all visited nodes. """ for n in self.g.get_all_v().values(): # Set all nodes as unvisited. n.temp_color = "WHITE" visited = [] queue = [] node.temp_color = "GRAY" visited.append(node.key) queue.append(node.key) if out: while queue: v = queue.pop(0) for neighbor in self.g.all_out_edges_of_node(v).keys(): if self.g.get_all_v()[neighbor].temp_color == "WHITE": self.g.get_all_v()[neighbor].temp_color = "GRAY" visited.append(neighbor) queue.append(neighbor) else: while queue: v = queue.pop(0) for neighbor in self.g.all_in_edges_of_node(v).keys(): if self.g.get_all_v()[neighbor].temp_color == "WHITE": self.g.get_all_v()[neighbor].temp_color = "GRAY" visited.append(neighbor) queue.append(neighbor) return visited
def dfs(self, node: Node, components, time, low_link, ids, on_stack, stack, translator, node_id=None): """ DFS algorithm implementation used by Tarjan's algorithm. :param node: starting node. :param components: the list of strongly connected components which need to be updated. :param time: to give each node an id. :param low_link: the low link values (The smallest node ids reachable from current node). :param ids: a unique id list for each node. :param on_stack: tracks if whether or not nodes are on the stack. :param stack: the stack containing the nodes. :param translator: translates node id to index. :param node_id: optional: search for specific node_id SCC. :return: a list of SCCs or a single SCC if node_id is specified. """ at = node.key # Denote the id of the node that we are currently at. stack.append(at) # Push current node to the stack. on_stack[translator[ at]] = True # Mark the current node as being on the stack. low_link[translator[at]] = ids[translator[ at]] = time # Give an id and a current low_link value to the node. time += 1 node.temp_color = "GRAY" # Mark current node as visited. if node.key in self.g.E: # If current_node has neighbors at all. for neighbor in self.g.E[ node.key].keys(): # For each neighbor of the current node. to = neighbor # Represents the id of the node that we are going to. if self.g.get_all_v( )[to].temp_color == "WHITE": # If the neighbor is unvisited. self.dfs(self.g.get_all_v()[to], components, time, low_link, ids, on_stack, stack, translator, node_id) if on_stack[translator[ to]]: # If the node that we are just came from is on the stack. low_link[translator[at]] = min(low_link[translator[at]], low_link[translator[to]]) # After visiting all neighbors of the current node. if ids[translator[at]] == low_link[ translator[at]]: # Check if we are on the beginning of a SCC. component = [] # Build a list which will hold the component. while True: x = stack.pop( ) # Pop all the nodes inside the strongly connected component. component.insert( 0, x) # Add this node to the beginning of the list of SCCs. on_stack[translator[ x]] = False # Mark node as no longer being on stack. low_link[translator[x]] = ids[translator[ at]] # Make sure all nodes on the same SCC have the same id. if x == at: # Once we reach the start of the SCC, break the loop. break if node_id is not None: # If SCC node_id is specified. if node_id in component: # If we found the desired SCC. return component # Return the component containing node_id. components.append( component) # Finally add the SCC to the list of SCCs.