Depth-first search in 4 minutes
Channel: Michael Sambol
Watch on YouTube →Algorithm • medium
DFS dives **as deep as possible** down each branch before backtracking. Implementation is either recursion (most natural) or an explicit stack.
It's the algorithmic shape behind cycle detection, topological sort, connected-component finding, maze-solving, and most backtracking problems.
Like BFS, it's O(V + E) but its memory profile is O(depth) on the call stack, often dramatically less than BFS's O(V) queue.
Typical Complexity Baseline
| Metric | Value |
|---|---|
| Time | O(V + E) |
| Space | O(V) recursion depth |
See It
A small animated SVG that illustrates the core idea the moving parts you need to keep in your head when you read the rest of the guide.
We follow one branch all the way down before returning to try a sibling.
Watch First
A quick visual walkthrough before the detailed sections pick the format that helps you learn fastest.
Channel: Michael Sambol
Watch on YouTube →Mechanics
The building blocks and terminology you need before everything else clicks. Skim or deep-read.
What it is The call stack acts as the implicit data structure. Each frame is a partial walk through the graph.
Why it matters Recursion (or stack) is a required building block for understanding how Depth-First Search (DFS) stays correct and performant on large inputs.
What it is Without it, DFS on a graph with cycles loops forever. Trees don't need it; graphs do.
Why it matters Visited set is a required building block for understanding how Depth-First Search (DFS) stays correct and performant on large inputs.
What it is Logic that runs when entering vs. leaving a node. Post-order is where topological sort and tree-DP happen.
Why it matters Pre/post hooks is a required building block for understanding how Depth-First Search (DFS) stays correct and performant on large inputs.
What it is white = unvisited, gray = on current DFS path, black = fully explored. A gray-to-gray edge is a back-edge a cycle.
Why it matters 3-color marking is a required building block for understanding how Depth-First Search (DFS) stays correct and performant on large inputs.
What it is When a node has no more unvisited neighbors, undo any state changes and return to the parent frame.
Why it matters Backtrack step is a required building block for understanding how Depth-First Search (DFS) stays correct and performant on large inputs.
What it is Process the node before its children. Used for serializing trees, copying structure.
Why it matters Process the node before its children. Used for serializing trees, copying structure. In Depth-First Search (DFS), this definition helps you reason about correctness and complexity when inputs scale.
What it is Process the node after all children. Used for tree DP, deletion, and topological sort.
Why it matters Process the node after all children. Used for tree DP, deletion, and topological sort. In Depth-First Search (DFS), this definition helps you reason about correctness and complexity when inputs scale.
What it is An edge from a descendant to an ancestor in the DFS tree its existence proves a cycle.
Why it matters An edge from a descendant to an ancestor in the DFS tree its existence proves a cycle. In Depth-First Search (DFS), this definition helps you reason about correctness and complexity when inputs scale.
What it is The implicit tree formed by the parent pointers in DFS. Subtree size = nodes reachable through that branch.
Why it matters The implicit tree formed by the parent pointers in DFS. Subtree size = nodes reachable through that branch. In Depth-First Search (DFS), this definition helps you reason about correctness and complexity when inputs scale.
What it is Same algorithm with an explicit stack. Useful when recursion depth would blow the default stack limit (~10⁴ in Python).
Why it matters Same algorithm with an explicit stack. Useful when recursion depth would blow the default stack limit (~10⁴ in Python). In Depth-First Search (DFS), this definition helps you reason about correctness and complexity when inputs scale.
Walkthrough
Every frame of the concept diagram, laid out top-to-bottom so you can scroll through the algorithm at your own pace.
Start at S. Mark visited. Stack = [S].
From S, push A (unvisited). Visit A.
From A, push B (unvisited). Visit B.
From B, push C (unvisited). Visit C.
From C, push D (unvisited). Visit D.
From D, push I (unvisited). Visit I.
I has no more unvisited children → pop. Back to D.
D has no more unvisited children → pop. Back to C.
C has no more unvisited children → pop. Back to B.
From B, push G (unvisited). Visit G.
G has no more unvisited children → pop. Back to B.
B has no more unvisited children → pop. Back to A.
From A, push E (unvisited). Visit E.
From E, push H (unvisited). Visit H.
H has no more unvisited children → pop. Back to E.
E has no more unvisited children → pop. Back to A.
A has no more unvisited children → pop. Back to S.
From S, push F (unvisited). Visit F.
F has no more unvisited children → pop. Back to S.
S has no more unvisited children → pop. Stack empty → done.
Tradeoffs
When this technique wins, when it loses, and what to reach for instead.
Choose this when Choose DFS when you need cycle detection, topological order, or any tree-shaped exploration. Also when memory is tight and the graph is deep but narrow.
Tradeoff DFS does **not** find shortest paths. The first time DFS reaches a node is generally not via the shortest path.
Choose this when Plain DFS when you don't care about depth.
Tradeoff IDDFS uses BFS-style optimal-depth guarantees with DFS's memory profile but at the cost of repeated work.
Choose this when DFS when you're exploring a graph; backtracking when you're exploring a constraint search tree.
Tradeoff Backtracking is essentially DFS plus 'undo step on return' for state same shape, different bookkeeping.
In Production
Where this shows up at real companies and what the on-call engineer learned the hard way.
Every modern build system runs DFS over a dependency DAG to compute the topological build order and uses gray/black marking to fail fast on circular dependencies.
Takeaway Cycle detection in build graphs is mission-critical; DFS makes it nearly free.
Type-checkers, lint rules, and dead-code-elimination passes traverse ASTs with recursive DFS. The post-order step is where most analysis happens (children analyzed before parent).
Takeaway Trees and ASTs are DFS's natural habitat postorder lets information flow up.
Recursive directory traversal is DFS. du (disk usage) is post-order DFS totaling subtree sizes only after children are fully measured.
Takeaway Tree-shaped problems with bottom-up aggregation map directly to post-order DFS.
Code
A clean reference implementation in TypeScript, Python, and Go. Switch languages with the toggle.
Standard recursive DFS that returns the visited set in DFS-finish order useful for topological sort and component labeling.
Complexity: Time O(V + E), Space O(V) call stack + visited
Recursive DFS with cycle-safe visited tracking
function depthFirstSearch(params: {
sourceNode: string
adjacencyByNode: Map<string, string[]>
}): string[] {
const visitedNodes = new Set<string>()
const dfsFinishOrder: string[] = []
function visitNode(currentNode: string): void {
if (visitedNodes.has(currentNode)) return
visitedNodes.add(currentNode)
for (const neighborNode of params.adjacencyByNode.get(currentNode) ?? []) {
visitNode(neighborNode)
}
dfsFinishOrder.push(currentNode)
}
visitNode(params.sourceNode)
return dfsFinishOrder
}
Watch Out
The bugs and edge cases that bite engineers most often. Skim before you ship.
Pro Tips
Small habits that pay off every time you reach for this pattern.
Pattern Recognition
Concrete signals that tell you this is the right pattern both at work and on LeetCode.
Practice
A curated easy-to-hard problem ladder. Each one reinforces a specific aspect of the pattern.
| # | Problem | Difficulty | Typical Complexity |
|---|---|---|---|
| 1 | Number of Islands | Medium | O(rows × cols) |
| 2 | Clone Graph | Medium | O(V + E) |
| 3 | Course Schedule | Medium | O(V + E), cycle detection |
| 4 | Pacific Atlantic Water Flow | Medium | O(rows × cols) |
| 5 | Path Sum II | Medium | O(n) |
| 6 | Word Search | Medium | O(rows × cols × 4^len) |
| 7 | Critical Connections in a Network | Hard | O(V + E), Tarjan's bridges |
| 8 | Longest Increasing Path in a Matrix | Hard | O(rows × cols), DFS + memo |