Graph DFS — cycles, components, and back-edges
Graph DFS
Depth-first search plunges as deep as possible along one path before backtracking. Where BFS explores in rings, DFS drills a tunnel — different order, different applications, same O(V + E) cost.
Two ways to write it
Recursive DFS is almost the definition. Iterative DFS uses an explicit stack — matters when depth exceeds Python's recursion limit (~1000 frames).
Connected components
Loop over every node. If unseen, run DFS from it — everything reached is one component. Count outer starts = number of components.
Cycle detection
Two flavours, easy to confuse:
- Undirected: cycle exists if DFS finds a neighbour that's already visited and isn't the parent it came from. Track
(node, parent)on stack. - Directed: track three colours — WHITE (unseen), GRAY (on current stack), BLACK (fully processed). A GRAY neighbour = back-edge = cycle. Plain 'visited' check is wrong for directed.
When DFS beats BFS
- Finding any path (not shortest).
- Topological sort.
- Strongly connected components.
- Backtracking search — permutations, N-queens, sudoku.
Common bugs to avoid
- Recursion depth on long chains. Switch to iterative or raise limit.
- Undirected cycle check without parent. Every edge
u-vlooks like a cycle. - Directed cycle check using just 'visited'. DAG paths that reconverge falsely report a cycle.
Try it
- Rewrite the undirected cycle check recursively.
- Add directed-graph cycle detector using WHITE/GRAY/BLACK. Test on
[(0,1),(1,2),(2,0)](cycle) vs[(0,1),(0,2),(1,2)](DAG).
3 graded exercises
Write the code, click Check. We’ll tell you exactly what to fix.
- Exercise 1
Write
dfs_order(graph, start)that returns nodes in the order a recursive DFS visits them. Visit neighbors in the order they appear in the adjacency list. - Exercise 2
Write
path_exists(graph, src, dst)returning True if there's any path fromsrctodst, else False. A node reaches itself. Use DFS. - Exercise 3
Write
count_components(graph)returning how many connected components the undirected graph has. Consider every key ingraphas a node, even ones with an empty neighbor list.