How to traverse cyclic directed graphs with modified DFS algorithm
Data Structures & Algorithms practice on Codemia
Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.
Introduction
Regular depth-first search already works on directed graphs, but it must track state carefully when cycles are present. Without that extra bookkeeping, recursion can loop forever or report misleading traversal results.
What DFS Must Track in a Cyclic Directed Graph
For an acyclic tree, a simple visited set is enough. For a cyclic directed graph, that is only part of the story. You often need three states:
- unvisited
- currently in the active recursion stack
- fully processed
These are commonly called:
- white
- gray
- black
Why does this matter? Because seeing an already visited node is not always the same thing as seeing a cycle edge. A node currently in the active stack means a back edge and therefore a cycle. A node that is fully processed does not.
Modified DFS with Colors
Here is a clean Python implementation.
This does three useful things:
- traverses all reachable nodes
- avoids infinite recursion on cycles
- records cycle information when a back edge is found
Why Gray Nodes Matter
Suppose the traversal goes from A to B to C, and then C points back to A.
At that moment:
- '
Ais gray' - '
Bis gray' - '
Cis gray'
Seeing an edge from C to A tells you the search found a path back into the current recursion chain. That is a cycle. If A were black instead, it would mean A had already been completely explored earlier, which is different.
This is the essential DFS modification for cyclic directed graphs.
If You Only Need Safe Traversal
If cycle reconstruction is unnecessary and you only want to visit each node once safely, the algorithm can be simpler.
This avoids infinite loops, but it loses the extra meaning of active-stack detection. So use this version only when safe traversal is enough and cycle classification is not required.
Recursive vs Iterative DFS
Recursive DFS is concise, but very deep graphs can hit recursion limits in Python or stack limits in other languages. An iterative version with an explicit stack is better when graph depth is unpredictable.
The same state concept still applies:
- maintain a visited or color map
- distinguish active exploration from finished processing when cycle detection matters
So the modification is conceptual, not tied to recursion specifically.
Common Uses
This pattern appears in:
- cycle detection in dependency graphs
- deadlock analysis
- graph validation before topological sorting
- compiler and build-system dependency checks
In all of those cases, "visited" is not enough. You need to know whether the node is being explored right now.
Common Pitfalls
Using only a visited set and assuming every repeated node implies a cycle gives false positives in directed graphs.
Forgetting to mark a node as fully processed after exploring its outgoing edges leaves the search state inconsistent.
Trying to reconstruct a cycle without a parent map makes the reporting logic much harder than it needs to be.
Using plain recursive DFS on a very deep graph can fail because of recursion depth even when the algorithm is logically correct.
Summary
- DFS on cyclic directed graphs needs more state than a simple visited set.
- The standard modification is a three-color or active-stack approach.
- A gray-to-gray edge during recursion indicates a cycle.
- Use a parent map if you want to reconstruct the actual cycle path.
- If you only need safe traversal, a visited set is enough, but it does less than full cycle-aware DFS.
Related reading
- How to turn integers into Fibonacci coding efficiently?
- How to understand Locality Sensitive Hashing?
- How to understand the dynamic programming solution in linear partitioning?
- How to understand the knapsack problem is NP-complete?
- How to understand a role of a queue in a distributed system?
- How to understand sess.as_default and sess.graph.as_default?
- how to Update a key in Priority Queue in Olog n time in dijkstra's algorithm?
- How to update element priorities in a heap for Prim's Algorithm?

DSA Fundamentals
Master algorithmic patterns and data structures through hands-on LeetCode-style problems - from arrays and hashing to dynamic programming and advanced graphs.
View the courseTrack what you have practised
A free account saves your progress, solutions and study plan across every problem on Codemia.
Data Structures & Algorithms practice on Codemia
Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.