To detect and output a cycle with DFS, just mark each vertex as you get to it; if any child of the current vertex is marked, you know you have a cycle involving that child. Furthermore you know that that child vertex is the first vertex belonging to this particular cycle that was encountered by the DFS, and that every move in the DFS since it first encountered that vertex (i.e. every recursive call since then that hasn't yet returned) has visited another vertex in the cycle. The only information you need to pass back up the call stack is this child vertex, or a special value indicating that no cycle was found. You can pass this back as a return value:
dfs(v, p) {
marked[v] = true
For each neighbour u of v:
If u != p: # I.e. we ignore the edge from our parent p
If marked[u]:
Append v to cycleVertices
Return u # Cycle!
Else:
result = dfs(u, v)
If result == FINISHED:
# Some descendant found a cycle; now we're just exiting
Return FINISHED
Else if result != NOCYCLE:
# We are in a cycle whose "top" vertex is result.
Append v to cycleVertices
If result == v:
return FINISHED # This was the "top" cycle vertex
Else:
return result # Pass back up
marked[v] = false # Not necessary, but (oddly?) not harmful either ;)
Return NOCYCLE
}
After calling dfs(r, nil)
for some vertex r
(and any non-vertex value nil
), cycleVertices
will be populated with a cycle if one was found.
[EDIT: As pointed out by Juan Lopes, unmarking vertices is not necessary, and is possibly confusing; but, interestingly, doesn't affect the time complexity for undirected graphs.]