Depth-First Search (DFS)
Table of Contents
Introduction
Depth-First Search (DFS) is a fundamental graph traversal algorithm that explores as far as possible along each branch before backtracking. Think of it like exploring a maze by always taking the first path you see, going as deep as possible, then backtracking when you hit a dead end.
DFS is essential for detecting cycles, topological sorting, finding connected components, and solving maze/puzzle problems. Unlike BFS which uses a queue, DFS uses a stack (or recursion, which implicitly uses the call stack).
Review Graphs, Stacks, and Binary Trees first if needed.
O(V + E) time and is particularly useful for detecting cycles, finding paths, and problems requiring backtracking or exhaustive exploration.
How DFS works
DFS explores a graph in depth:
- Start at a source vertex
- Explore one neighbor completely before moving to the next
- Recursively visit unvisited neighbors
- Backtrack when no unvisited neighbors remain
Visual example:
Graph:
A --- B --- E
| |
C --- D
DFS from A (one possible order):
Visit A → go to B → go to E (dead end, backtrack)
→ back to B → go to D → go to C (already visited A)
→ back to D (dead end) → done
Order: A, B, E, D, C
DFS algorithm
Key components:
- Stack: Maintains vertices to visit (LIFO) - or use recursion
- Visited set: Tracks visited vertices to avoid cycles
- Backtracking: Return to previous vertex when stuck
Steps:
- Mark starting vertex as visited
- For each unvisited neighbor:
- Recursively visit that neighbor
- Backtrack when all neighbors are visited
DFS implementation
Recursive DFS on a graph
Iterative DFS using stack
DFS on a binary tree
Common DFS problems
1. Detect cycle in undirected graph
2. Find all paths from source to target
3. Topological sort (directed acyclic graph)
function topologicalSort(graph) {
const visited = new Set();
const stack = [];
function dfs(vertex) {
visited.add(vertex);
for (const neighbor of graph.adjacencyList.get(vertex) || []) {
if (!visited.has(neighbor)) {
dfs(neighbor);
}
}
stack.push(vertex); // Add to stack on the way back up
}
// Visit all vertices
for (const vertex of graph.adjacencyList.keys()) {
if (!visited.has(vertex)) {
dfs(vertex);
}
}
return stack.reverse(); // Reverse to get topological order
}
DFS vs BFS comparison
| Feature | DFS | BFS |
|---|---|---|
| Data structure | Stack/Recursion (LIFO) | Queue (FIFO) |
| Order | Depth first | Level by level |
| Shortest path | ❌ No | ✅ Yes (unweighted) |
| Space | O(height) | O(width) |
| Use cases | Cycle detection, topological sort, pathfinding | Shortest path, level-order |
| Backtracking | ✅ Natural | ❌ Not typical |
Complexity analysis
- Time: O(V + E) where V = vertices, E = edges
- Space:
- Recursive: O(V) for call stack + visited set
- Iterative: O(V) for explicit stack + visited set
Common pitfalls
- Stack overflow: Deep recursion on large graphs
- Not marking visited: Leads to infinite loops
- Forgetting to backtrack: In path-finding problems
- Confusing pre/post order: Know when to process vertices
- Not handling disconnected components: May miss vertices
When to use DFS
- Detecting cycles in graphs
- Topological sorting of directed acyclic graphs
- Finding connected components
- Pathfinding with backtracking
- Solving mazes and puzzles
- Tree traversals (in-order, pre-order, post-order)
- Generating permutations/combinations
Practice problems
- Clone a graph (DFS)
- Number of islands (2D grid DFS)
- Course schedule (detect cycle with DFS)
- Surrounded regions (capture regions on board)
- Pacific Atlantic water flow
- Longest increasing path in matrix
- Word search (backtracking with DFS)
- Validate binary search tree
- Path sum in binary tree
- Serialize and deserialize binary tree
- All paths from source to target
- Keys and rooms
- Reconstruct itinerary
- Employee importance (DFS on graph)
- Lowest common ancestor in binary tree
Complexity summary
- Time:
O(V + E)— visit each vertex and edge once - Space:
O(V)— visited set and recursion stack/explicit stack
DFS is essential for graph problems requiring deep exploration, cycle detection, and backtracking. Master both DFS and BFS to handle the full range of graph and tree traversal problems.