The recursion call stack is the traversal stack. That's why DFS is so short to write recursively — the language manages the stack for you. Two moments matter:
Recursive is cleaner and preferred in interviews unless depth can exceed the stack limit (~10⁴–10⁵ frames in Java). For very deep graphs, convert to an explicit stack. Note: an iterative stack-based DFS visits nodes in a slightly different order than recursion, which is fine for reachability but matters if you rely on strict pre/post ordering.
To enumerate all paths (not just reachability), add the node to a path list before recursing and remove it after — that "undo" is backtracking. Here visited marking is often not used (you may revisit a node on a different path).
| Metric | Value | Why |
|---|---|---|
| Time | O(V + E) | Each node/edge touched once (for plain traversal). |
| Space | O(V) | Recursion stack depth + visited set. |
Recursive DFS (grid flood variant) plus the iterative form for reference.
import java.util.*;
/** Recursive DFS on a grid — counts cells in one region. */
public int dfs(int[][] grid, int r, int c, boolean[][] seen) {
int rows = grid.length, cols = grid[0].length;
if (r < 0 || r >= rows || c < 0 || c >= cols) return 0; // bounds
if (seen[r][c] || grid[r][c] == 0) return 0; // visited or water
seen[r][c] = true; // pre-order: mark on entry
int count = 1;
count += dfs(grid, r+1, c, seen);
count += dfs(grid, r-1, c, seen);
count += dfs(grid, r, c+1, seen);
count += dfs(grid, r, c-1, seen);
return count; // post-order: aggregate on the way up
}
/** Iterative DFS on an adjacency list — when recursion depth is a risk. */
public void dfsIterative(List<Integer>[] adj, int start) {
boolean[] seen = new boolean[adj.length];
Deque<Integer> stack = new ArrayDeque<>();
stack.push(start);
while (!stack.isEmpty()) {
int u = stack.pop();
if (seen[u]) continue; // mark on POP here (may be pushed twice)
seen[u] = true;
for (int v : adj[u]) if (!seen[v]) stack.push(v);
}
}
The tell is reachability, structure, exhaustive enumeration, or "explore a region." Common disguises:
| Disguise / phrasing | What's really asked | Twist to handle |
|---|---|---|
| "Number of islands / regions / provinces" | Flood-fill connected regions | DFS from each unvisited seed; count restarts |
| "Area / perimeter / shape of a region" | Aggregate in post-order | Return counts up the recursion |
| "All paths from A to B" | Exhaustive enumeration | Backtracking: add→recurse→remove; no global visited |
| "Clone graph / deep copy" | DFS with a visited→copy map | Memoize created nodes to handle cycles |
| "Does a path/target exist?" | Reachability | Early-return on hit; mark visited to avoid revisits |
| "Surrounded regions / capture" | DFS from borders inward | Mark border-connected as safe, flip the rest |
| "Count connected components" | DFS in an outer loop (Day 4) | Restart count = component count |
| "Very deep chain, n up to 10⁵" | Recursion-depth trap | Convert to iterative explicit-stack DFS |