Search code examples
algorithmgraphcycledirected-graph

Print all the cycles in a directed graph


We can use the algorithm stated here to find the number of cycles in a directed graph. I need to understand the algorithm.

dfs(adj,node,visited):  
  if (visited[node]):  
    if (node == start):  
      "found a path"  
    return;  
  visited[node]=YES;  
  for child in adj[node]:  
    dfs(adj,child,visited)
  visited[node]=NO; #<- why?

(1) What is the use of that last statement exactly? A short description of how the algo works would be so helpful. Since the algorithm is basically to count the num of cycles from a node back to the same node, we can use another array, call it v and do the following trick :

def dfs(adj,node,start,visited):
    if (node in v):
        return

and then write all other things intact. As part of the main function,

for i in range(len(adj)):
    dfs(adj,i,i,visited)
    v.append(i)

does the job neatly. So basically we set the visited[node] of the elements (nodes) of an already established cycle to 0 (false) forever. Not a good time complexity, but it works nonetheless.

For printing the array, my plan was to put all the elements in an array (call it A) and keep appending until a path is found. Now when the path is found, backtrack from start (now A[last_elem]) to start (which is A[some_prev_elem]). Then remove the elements from A upto the position where the recursion is supposed to continue (for example, 0->1->2->0 and 0->1->3->.. are two branches of the dfs tree, then we remove upto only the last two elements of A (which are 2 and 0) since the recursion continues from 1 now).

(2) I cannot implement the algorithm I just wrote. This is the main question but I think I need to understand (1) above to understand the code for printing all the cycles.

I understand that there are algorithms on the internet, I am trying to use this algorithm.


Solution

  • Let's try to draw a graph/tree and a call stack of DFS. In my opinion, the clue to understanding here is to keep track of how "visited" changes. For instance:

    a tree

    |step |node |visited
    |1    |1    |{1: yes}     
    |2    |2    |{1: yes, 2: yes}
    |3    |6    |{1: yes, 2: yes, 6: yes}
    |4    |7    |{1: yes, 2: yes, 6: no, 7: yes}
    ...
    

    Here's an interesting part, please, pay attention to how 6's been changed in visited on 4th step. We just backtracked from 6th node to 2nd again and that's why 6 is not within the current path.

    So, if we truly found a node in visited it means we've been going deeper and deeper without backtracking and found the node once again, which means it's a cycle.

    In my example, that is what's going to happen to node 1 eventually, and you can check it if you continue to populate the table of DFS calls.