BFS vs DFS

BFS vs DFS broken down with side-by-side traces on the same graph. Know exactly which traversal to pick for any interview problem.

10 minutes
Medium
Intermediate

What you will learn

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

Most engineers 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.

TL;DR
BFS guarantees shortest path in unweighted graphs because it processes nodes by distance. DFS explores depth-first and naturally exposes cycle detection, topological ordering, and connectivity. The data structure (queue vs stack) creates the invariant, and the invariant determines which problems each algorithm can solve.

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

Take a graph with 6 nodes and edges 0→1, 0→2, 1→3, 1→4, 2→4, 2→5. The source node is 0.

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.”
BFS vs DFS invariant

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.

ℹ️ Info
BFS's memory usage grows with the graph's width. DFS's memory usage grows with the graph's depth. Neither is universally better. The graph's shape determines which is more efficient.

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.
BFS is the right choice
DFS is the right choice
Shortest path in unweighted graphs
Cycle detection (back edge to stack ancestor)
Level-order traversal (layer by layer)
Topological sort (finish-time ordering)
Nearest target or minimum steps
Connected components (flood fill)
Problems asking for "minimum number of moves"
Path enumeration and backtracking

Two Interview Problems, Two Traversal Choices

Problem 1, minimum steps in a grid. You're given a 2D grid where 0 is passable and 1 is a wall. Find the minimum number of steps from the top-left corner to the bottom-right corner, moving in four directions.

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.

Problem 2, detect a cycle in a directed graph. Given a directed graph, determine whether it contains a cycle.

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.

💡 Tip
In interviews, the trigger word "minimum" in an unweighted graph almost always signals BFS. The trigger words "detect," "order," or "all paths" signal DFS.

Going Deeper

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.

Do you want to master data structures?

Try our data structures learning path made of highly visual and interactive courses. Get hands on experience by solving real problems in a structured manner. All resources you would ever need in one place for FREE

BFS can't guarantee shortest paths in weighted graphs. It processes nodes in hop count order, not cumulative weight order, so it might return a path with fewer edges but a longer total distance. For weighted graphs, Dijkstra's algorithm is the right choice because it uses a priority queue to process nodes by total weight from the source. You'd only reach for BFS on a weighted graph if all edges happened to share the same weight, which would make it functionally unweighted.
DFS can be implemented either recursively or iteratively with an explicit stack. The recursive version is more elegant but can hit stack overflow limits on deep graphs with 10,000+ nodes. Iterative DFS with a manual stack avoids that limit while producing the same traversal order, and it gives you more control over the stack size.
Both work equally well for simple path existence checks, and both run in O(V + E) time. DFS is slightly easier to write recursively for this case.
DFS works because its finish times encode dependency order. A node finishes only after all its descendants have finished, so reversing finish times gives a valid topological ordering. BFS can actually perform topological sort too, using Kahn's algorithm where you process nodes with zero in-degree first, then remove their edges and repeat. Both approaches are correct, but DFS-based topological sort is the more commonly tested version in coding interviews because it fits naturally into the same DFS traversal you'd already be writing for cycle detection.
Both algorithms have identical time complexity of 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.
Was this helpful?