// Tarjan_SCC // * visit all nodes with DFS // * compute found[node] and low_link[node] // * extract SCCs // // nodes = list of all nodes from G // adj[node] = the adjacency list of node // example: adj[node] = {..., neigh, ...} => edge (node, neigh) TARJAN_SCC(G = (nodes, adj)) { // STEP 1: initialize results // parent[node] = parent of node in the DFS traversal // // the timestamp when node was found (when started to visit its subtree) // Note: The global timestamp is incremented everytime a node is found. // // the minimum accessible timestamp that node can see/access // low_link[node] = min { found[x] | x is node OR x in ancestors(node) OR x in descendants(node) }; // foreach (node in nodes) { parent[node] = null; // parent not yet found found[node] = +oo; // node not yet found low_link[node] = +oo; // value not yet computed } nodes_stack = {}; // visiting order stack // STEP 2: visit all nodes timestamp = 0; // global timestamp foreach (node in nodes) { if (parent[node] == null) { // node not visited parent[node] = node; // convention: the parent of the root is actually the root // STEP 3: start a new DFS traversal this subtree DFS(node, adj, parent, timestamp, found, low_link, nodes_stack); } } } DFS(node, adj, parent, ref timestamp, found, low_link, nodes_stack) { // STEP 1: a new node is visited - increment the timestamp found[node] = ++timestamp; // the timestamp when node was found low_link[node] = found[node]; // node only knows its timestamp nodes_stack.push(node); // add node to the visiting stack // STEP 2: visit each neighbour foreach (neigh in adj[node]) { // STEP 3: check if neigh is already visited if (parent[neigh] != null) { // STEP 3.1: update low_link[node] with information gained through neigh // note: neigh is in the same SCC with node only if it's in the visiting stack; // otherwise, neigh is from other SCC, so it should be ignored if (neigh in nodes_stack) { low_link[node] = min(low_link[node], found[neigh]); } continue; } // STEP 4: save parent parent[neigh] = node; // STEP 5: recursively visit the child subtree DFS(neigh, adj, parent, timestamp, found, low_link, nodes_stack); // STEP 6: update low_link[node] with information gained through neigh low_link[node] = min(low_link[node], low_link[neigh]); } // STEP 7: node is root in a SCC if low_link[node] == found[node] // (there is no edge from a descendant to an ancestor) if (low_link[node] == found[node]) { // STEP 7.1: pop all elements above node from stack => extract the SCC where node is root new_scc = {}; do { x = nodes_stack.pop(); new_scc.push(x); } while (x != node); // stop when node was popped from the stack // STEP 7.2: save / print the new SCC print(new_scc); } }