DFS vs BFS, When to Use Which?
Channel: AlgoMonster
Watch on YouTube →Algorithm • hard
Depth-first search (DFS) and breadth-first search (BFS) are the two foundational graph traversal strategies.
BFS expands in layers from a source node, while DFS goes deep along one branch before backtracking.
The most important correctness rule for both is visited-state tracking to avoid revisiting cycles.
Typical Complexity Baseline
| Metric | Value |
|---|---|
| Complexity Note 1 | O(V + E) |
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.
Visited set prevents revisits. Order depends on whether you queue (BFS) or stack (DFS).
Watch First
A quick visual walkthrough before the detailed sections pick the format that helps you learn fastest.
Channel: AlgoMonster
Watch on YouTube →Mechanics
The building blocks and terminology you need before everything else clicks. Skim or deep-read.
What it is Entity/node in graph.
Why it matters Vertex is a required building block for understanding how Depth vs Breadth First Search (DFS/BFS) stays correct and performant on large inputs.
What it is Connection between vertices.
Why it matters Edge is a required building block for understanding how Depth vs Breadth First Search (DFS/BFS) stays correct and performant on large inputs.
What it is Compact representation for sparse graphs.
Why it matters Adjacency list is a required building block for understanding how Depth vs Breadth First Search (DFS/BFS) stays correct and performant on large inputs.
What it is Tracks explored nodes to prevent loops.
Why it matters Visited set is a required building block for understanding how Depth vs Breadth First Search (DFS/BFS) stays correct and performant on large inputs.
What it is Stack/queue holding next nodes to process.
Why it matters Traversal frontier is a required building block for understanding how Depth vs Breadth First Search (DFS/BFS) stays correct and performant on large inputs.
What it is Maximal set of nodes connected by paths.
Why it matters Maximal set of nodes connected by paths. In Depth vs Breadth First Search (DFS/BFS), this definition helps you reason about correctness and complexity when inputs scale.
What it is Edges have direction.
Why it matters Edges have direction. In Depth vs Breadth First Search (DFS/BFS), this definition helps you reason about correctness and complexity when inputs scale.
What it is Edges are bidirectional.
Why it matters Edges are bidirectional. In Depth vs Breadth First Search (DFS/BFS), this definition helps you reason about correctness and complexity when inputs scale.
What it is Nodes reachable in equal number of edges from source.
Why it matters Nodes reachable in equal number of edges from source. In Depth vs Breadth First Search (DFS/BFS), this definition helps you reason about correctness and complexity when inputs scale.
What it is Traversal tree induced by depth-first exploration.
Why it matters Traversal tree induced by depth-first exploration. In Depth vs Breadth First Search (DFS/BFS), 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.
BFS from A. Visit A.
Visit B.
Visit C.
Visit D.
Visit E.
Visit H.
Visit F.
Visit G.
Visit I.
Tradeoffs
When this technique wins, when it loses, and what to reach for instead.
Choose this when Choose graph traversal when cycles or arbitrary connectivity exist.
Tradeoff Tree traversal is simpler because no cycles by definition.
Choose this when Choose plain BFS/DFS for unweighted reachability/connectivity.
Tradeoff Weighted shortest paths need Dijkstra or variants.
Choose this when Choose traversal when you need explicit paths/order exploration.
Tradeoff Union-Find is faster for repeated connectivity queries after unions.
In Production
Where this shows up at real companies and what the on-call engineer learned the hard way.
People-you-may-know and network analysis features depend on graph exploration patterns.
Takeaway Traversal logic powers social graph product features.
City, route, and region recommendation tasks rely on graph-like connectivity models.
Takeaway Graph traversals support practical recommendation pathing.
Dependency graphs and CI relationship analysis use traversal to find impact radius.
Takeaway Traversal is key for change impact tooling.
Code
A clean reference implementation in TypeScript, Python, and Go. Switch languages with the toggle.
Implement both traversals from scratch with explicit queue/stack buffers and boolean visited arrays so you can see exactly how frontier expansion differs.
Complexity: Time O(V+E), Space O(V) for both BFS and DFS (iterative)
BFS and DFS traversal from source
function bfsOrder(adjacencyByNode: number[][], startNode: number): number[] {
const visitedNodes = new Array<boolean>(adjacencyByNode.length).fill(false)
const queueBuffer = new Array<number>(Math.max(1, adjacencyByNode.length * 2)).fill(0)
let queueHeadIndex = 0
let queueTailIndex = 0
const traversalOrder: number[] = []
visitedNodes[startNode] = true
queueBuffer[queueTailIndex] = startNode
queueTailIndex += 1
while (queueHeadIndex < queueTailIndex) {
const currentNode = queueBuffer[queueHeadIndex]
queueHeadIndex += 1
traversalOrder.push(currentNode)
const neighborNodes = adjacencyByNode[currentNode] ?? []
for (const neighborNode of neighborNodes) {
if (visitedNodes[neighborNode]) {
continue
}
visitedNodes[neighborNode] = true
queueBuffer[queueTailIndex] = neighborNode
queueTailIndex += 1
}
}
return traversalOrder
}
function dfsOrder(adjacencyByNode: number[][], startNode: number): number[] {
const visitedNodes = new Array<boolean>(adjacencyByNode.length).fill(false)
const nodeStack: number[] = [startNode]
const traversalOrder: number[] = []
while (nodeStack.length > 0) {
const currentNode = nodeStack.pop()!
if (visitedNodes[currentNode]) {
continue
}
visitedNodes[currentNode] = true
traversalOrder.push(currentNode)
const neighborNodes = adjacencyByNode[currentNode] ?? []
for (let neighborIndex = neighborNodes.length - 1; neighborIndex >= 0; neighborIndex -= 1) {
const neighborNode = neighborNodes[neighborIndex]
if (!visitedNodes[neighborNode]) {
nodeStack.push(neighborNode)
}
}
}
return traversalOrder
}
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 | Find if Path Exists in Graph | Easy | O(V+E) |
| 2 | Number of Provinces | Medium | O(V^2) |
| 3 | Number of Islands | Medium | O(m*n) |
| 4 | Clone Graph | Medium | O(V+E) |
| 5 | Course Schedule | Medium | O(V+E) |
| 6 | Pacific Atlantic Water Flow | Medium | O(m*n) |
| 7 | Graph Valid Tree | Medium | O(V+E) |
| 8 | Word Ladder | Hard | O(V+E) |
| 9 | Alien Dictionary | Hard | O(V+E) |
| 10 | Critical Connections in a Network | Hard | O(V+E) |