BFS vs DFS: When to Use Each Traversal
BFS vs DFS broken down with side by side traces on the same graph. Know exactly which traversal to pick for any interview problem.
How BFS and DFS maintain different invariants using queue vs stack
Why BFS guarantees shortest path in unweighted graphs but DFS does not
A side by side trace of both algorithms on the same graph
When to use BFS for minimum steps vs DFS for cycle detection
It's tempting to treat BFS vs DFS as a matter of preference. Two ways to visit every node, pick whichever you remember. That works until an interview problem punishes the wrong choice with a time limit exceeded or a missing shortest path guarantee.
They aren't variations of the same idea. They maintain different invariants, use different collections, and solve different classes of problems. Which one you pick comes down to which invariant the problem requires.
What BFS and DFS actually do
Breadth first search processes all nodes at distance 1 from the source before any node at distance 2, all nodes at distance 2 before distance 3, and so on. It uses a queue to maintain this distance order invariant. You get a level by level sweep outward from the starting node.
Depth first search processes a single path as deep as possible before backtracking. It uses a stack (or the call stack via recursion) to remember where it came from. One branch gets fully exhausted before another is tried.
That distinction sounds academic until it costs you an interview. BFS's distance order invariant means the first time it reaches a node, that path is guaranteed to be the shortest (in unweighted graphs). DFS makes no such guarantee. It might reach a node through a 7-edge path before finding a 2-edge path. BFS can't miss the shorter route because it processes all 2-edge paths before any 7-edge path.
DFS does something else entirely. Because it fully explores one branch before starting another, it naturally detects back edges (cycles), computes finish times (topological ordering), and identifies connected components. BFS's level by level sweep doesn't expose these properties as cleanly.
Both algorithms on the same graph
BFS trace, using a queue.
`
Step 1: Queue = [0] → Process 0, enqueue 1, 2
Step 2: Queue = [1, 2] → Process 1, enqueue 3, 4
Step 3: Queue = [2, 3, 4] → Process 2, enqueue 5 (4 already visited)
Step 4: Queue = [3, 4, 5] → Process 3 (no new neighbours)
Step 5: Queue = [4, 5] → Process 4 (no new neighbours)
Step 6: Queue = [5] → Process 5 (no new neighbours)
`The visit order is 0, 1, 2, 3, 4, 5. All nodes appear in exact distance order from the source. Everything at distance 1 (nodes 1, 2) comes before distance 2 (nodes 3, 4, 5).
DFS trace, using a stack.
`
Step 1: Stack = [0] → Process 0, push 2, 1
Step 2: Stack = [2, 1] → Process 1, push 4, 3
Step 3: Stack = [2, 4, 3] → Process 3 (no new neighbours)
Step 4: Stack = [2, 4] → Process 4 (no new neighbours)
Step 5: Stack = [2] → Process 2, push 5
Step 6: Stack = [5] → Process 5 (no new neighbours)
`The visit order is 0, 1, 3, 4, 2, 5. Node 3 (distance 2) gets processed before node 2 (distance 1). DFS doesn't prioritize distance at all. It cares about depth, committing to one branch and exhausting it before backtracking.
“Same graph, same starting node, different data structure, different visit order. The queue creates a distance guarantee. The stack doesn't.”
Why the data structure matters
The queue is the entire reason BFS works for shortest paths. A queue is first in, first out. When you enqueue all neighbours at distance d, they sit behind any remaining distance d-1 nodes but ahead of all future distance d+1 nodes. The queue enforces distance ordering without any explicit distance tracking.
The stack is last in, first out. When you push neighbours, the most recently pushed node gets processed next. That's what creates the depth first behaviour. You keep diving deeper along whichever neighbour was pushed last, and you don't come back to earlier choices until the current path is exhausted. Memory follows the same split.
BFS stores the entire frontier at once. In a balanced tree with branching factor b and depth d, BFS holds O(b^d) nodes in the queue at the widest level. DFS holds at most O(d) nodes on the stack, one per level of depth. For deep, narrow graphs, DFS is far more memory efficient. For shallow, wide graphs, either works.
DFS also has an elegance in recursive form that BFS doesn't match. When you write DFS recursively, the call stack is the stack. You don't manage any explicit collection. The function calls handle backtracking automatically. That recursive form maps cleanly onto problems with recursive character: trees, expressions, nested configurations. BFS doesn't have an equivalent recursive form because queues aren't built into the call stack. That's why DFS often feels more natural when the problem itself is recursive.
When to use BFS vs DFS
It comes down to what the problem is asking for.
Use BFS when the problem needs:
- Shortest path in an unweighted graph: Minimum steps in a grid, shortest word transformation, nearest distance to a target. If the problem says "minimum" and every edge has equal weight, BFS is the answer.
- Level order processing: Anything that requires processing nodes layer by layer (binary tree level order traversal, finding all nodes at distance K).
- Nearest neighbour or closest target: BFS finds the closest match first because it explores in distance order.
Use DFS when the problem needs:
- Cycle detection: DFS tracks which nodes are currently on the recursion stack. A back edge to a node still on the stack means a cycle exists. BFS can detect cycles too, but DFS's stack based tracking is more natural for directed graphs.
- Topological ordering: DFS finish times, reversed, give a valid topological sort for directed acyclic graphs. This is the standard algorithm.
- Connected components: Run DFS from an unvisited node and mark everything reachable. Run again from the next unvisited node. Each run discovers one component.
- Exhaustive path enumeration: Backtracking problems, source to target paths, and Hamiltonian paths all use DFS because it explores one complete path before trying alternatives.
Two interview problems, two traversal choices
This is a BFS problem. Every move has equal weight (one step), so BFS's distance order guarantee applies. You enqueue the starting cell, then process level by level. The first time you reach the destination, you've found the shortest path. If you tried DFS here, it might find a path of length 30 before a path of length 8. DFS has no mechanism to prefer shorter paths.
This is a DFS problem. You track three states per node: unvisited, in_progress (on the current recursion stack), and completed. If DFS reaches a node that's still in_progress, you've found a back edge, which means a cycle. The recursion stack naturally maintains this "currently being explored" state. BFS can detect cycles in undirected graphs by checking parent relationships, but for directed graphs, DFS's stack based state tracking is the standard approach.
The same question applies to every graph problem. Does the problem need a distance guarantee or a graph property? Distance guarantee points to BFS. Graph property (cycles, ordering, connectivity) points to DFS.
Common traps when picking the wrong traversal
Knowing when to use BFS vs DFS is only half the battle. You also need to catch yourself before committing to the wrong one. Three mistakes come up repeatedly in interview settings.
- DFS for shortest path: You see a grid, you think "graph traversal," and you write DFS because it's faster to code recursively. DFS will find a path, but not necessarily the shortest one. You won't get a wrong answer error either. You'll get a path that's valid but suboptimal, which is harder to debug under time pressure. If the problem says "minimum moves," "fewest steps," or "shortest distance" and every edge has equal cost, that's your signal to reach for BFS.
- BFS for directed cycle detection: BFS can detect cycles in undirected graphs by checking whether a neighbour was already visited and isn't the parent. But in directed graphs, that logic breaks. A visited node in BFS might have been reached from a completely different branch, not from the current path. DFS's three state tracking (
unvisited,in_progress,completed) distinguishes between "this node is on the current path" and "this node was fully processed earlier." That distinction is what makes DFS the standard for directed cycle detection. - BFS with variable edge weights: BFS assumes every edge costs the same. The moment edges carry different weights, BFS's distance ordering invariant no longer holds. Processing nodes level by level gives you hop count order, not weight order. You need Dijkstra's algorithm (or Bellman-Ford if weights can be negative) to handle weighted shortest paths correctly.
All three traps share a root cause. You default to whichever traversal you practiced most recently instead of reading the problem constraints first. The fix is simple: before writing any code, identify whether the problem needs a distance guarantee or a structural property. That one question eliminates most wrong traversal choices.
Patterns that combine both traversals
Some interview problems don't fit cleanly into "use BFS" or "use DFS." They need one traversal for preprocessing and the other for the main query.
- Topo sort + BFS: In a directed acyclic graph with weighted edges, you can use DFS to compute a topological ordering and then relax edges in that order. This runs in
O(V + E)and works even with negative weights, unlike Dijkstra's. The DFS handles the structural ordering. The relaxation pass handles the distance computation. - Distance + enumeration: Problems that ask "find all shortest paths" often need both. BFS first determines the shortest distance to every node. Then DFS backtracks from the destination, only following edges where the distance decreases by exactly one. BFS provides the distance map. DFS explores every valid combination within that map.
- Multi-source BFS + DFS: Grid problems sometimes ask you to find the nearest distance from every cell to a set of source cells, and then classify regions based on those distances. Multi-source BFS (starting with all sources in the queue simultaneously) computes the distance grid in one pass. DFS or flood fill then processes the regions.
These combinations aren't exotic. They show up in problems tagged medium and hard on most platforms. The key insight is that BFS and DFS aren't competing tools. They answer different questions about the same graph, and harder problems often need both answers.
Beyond the two traversals
Every graph pattern you'll hit in interviews (connected components, two colouring, Dijkstra's algorithm, topological sort) builds on one or both of these traversals.
Codeintuition's Graph course traces both algorithms frame by frame with 289 illustrations, then builds five pattern families on top of them. The BFS traversal lesson walks through the queue state at every step, and the DFS pattern identification lesson trains you to recognise which problems require depth first exploration before you start coding.
Want to see BFS and DFS traced frame by frame?
Codeintuition's Graph course walks through queue and stack state at every step across 289 illustrations. Learn the identification triggers for when BFS guarantees shortest path vs when DFS exposes graph properties. Start with FREE prerequisite courses.
O(V + E) time. DFS is slightly easier to write recursively for this case.O(V + E), where V is vertices and E is edges. The difference shows up in space. BFS uses O(V) space proportional to the widest level of the graph, while DFS uses O(V) space proportional to the maximum depth. In practice, BFS tends to use more memory on wide graphs, and DFS tends to use more memory on deep, narrow ones.