Directed Graph Cycle
DFS with three colours: unseen, active (on the call stack), done
In an undirected graph, reaching an already-visited node is enough to declare a cycle. A directed graph needs one more bit per node, because two separate paths can legitimately converge on the same target without looping — that is a cross-edge, not a cycle. The fix is to track which nodes are ACTIVE right now, meaning still open on the current DFS call path, versus DONE, meaning fully explored and safe to converge on. A cycle exists exactly when an edge points at a node that is still active — a back-edge into your own ancestry.
Details
Animated on: 7 vertices with directed edges 0->1, 1->2, 2->3, 2->6, 3->4, 4->5, 5->3 — does the graph contain a cycle?
Problem:Directed Graph CycleMedium
DFS with three colours: unseen, active (on the call stack), done
step 1 / 15
Vertices and directed edges
7 vertices and 7 directed edges. A cycle means some node can reach itself by following arrows forward — DFS finds one the moment it revisits a node that is still open on its own current path.
1FUNCTION hasCycle(n, edges):2 BUILD adjacency list next FROM edges3 color <- ARRAY of n zeros4 FOR u FROM 0 TO n - 15 IF color[u] = 0 AND dfs(u, next, color) = true6 RETURN true7 RETURN false8FUNCTION dfs(u, next, color):9 color[u] <- 110 FOR EACH v IN next[u]11 IF color[v] = 112 RETURN true13 IF color[v] = 0 AND dfs(v, next, color) = true14 RETURN true15 color[u] <- 216 RETURN false
← / → step · space play · Home restart
Every step in words
Show all 15 steps
- 7 vertices and 7 directed edges. A cycle means some node can reach itself by following arrows forward — DFS finds one the moment it revisits a node that is still open on its own current path.
- Enter 0 and mark it active — it is now open on the call stack [0]. A back-edge INTO any of these active nodes would close a loop.
- 0 has an edge to 1, currently unseen.
- Enter 1 and mark it active — it is now open on the call stack [0, 1]. A back-edge INTO any of these active nodes would close a loop.
- 1 has an edge to 2, currently unseen.
- Enter 2 and mark it active — it is now open on the call stack [0, 1, 2]. A back-edge INTO any of these active nodes would close a loop.
- 2 has an edge to 3, currently unseen.
- Enter 3 and mark it active — it is now open on the call stack [0, 1, 2, 3]. A back-edge INTO any of these active nodes would close a loop.
- 3 has an edge to 4, currently unseen.
- Enter 4 and mark it active — it is now open on the call stack [0, 1, 2, 3, 4]. A back-edge INTO any of these active nodes would close a loop.
- 4 has an edge to 5, currently unseen.
- Enter 5 and mark it active — it is now open on the call stack [0, 1, 2, 3, 4, 5]. A back-edge INTO any of these active nodes would close a loop.
- 5 has an edge to 3, currently active.
- 3 is ACTIVE, meaning it is an ancestor of 5 on the current path — the edge 5 -> 3 closes the loop 3 -> 4 -> 5 -> 3. That is a cycle; no need to look further.
- The search stopped as soon as the back-edge 5 -> 3 appeared, so node 6 was never even visited — one back-edge is proof enough. RETURN true.
Now solve it yourself
Watching is the first half. The second is writing it without the animation.