Cloud Tech by Victor
AlgorithmsIntermediate

Graph Traversal: BFS & DFS

Why breadth-first search's queue explores every neighbor before any of their children, guaranteeing the shortest path in an unweighted graph, while depth-first search's stack (or recursion) does the opposite and can't make that guarantee at all.

Updated 2026-07-243 min read

Overview

Breadth-first search explores every neighbor of a vertex before any of those neighbors' own outgoing edges, implemented with a queue so vertices are processed in strict discovery order, level by level outward from the start. Depth-first search does the reverse, considering a vertex's children before its siblings, plunging as deep as possible along one path before backtracking, typically implemented via recursion (using the call stack implicitly) or an explicit stack. That structural difference has a direct, provable consequence: BFS's level-by-level ordering guarantees the first time it reaches any vertex is via a shortest path in an unweighted graph, while DFS has no such property at all and can reach a target via a long path long before a much shorter one exists. Both run in O(V + E) time for an adjacency-list graph; the choice between them is about which traversal order the actual problem needs, not raw speed.

Quick Reference

BFSDFS
OrderNeighbors before their childrenChildren before siblings
StructureQueueStack (or recursion)
Shortest path (unweighted)?GuaranteedNot guaranteed
Natural fit forShortest path, level-order processingBacktracking, cycle detection, topological sort

Syntax

python
from collections import deque

def bfs(graph, start):
    visited, queue = {start}, deque([start])
    while queue:
        v = queue.popleft()
        for n in graph[v]:
            if n not in visited:
                visited.add(n)
                queue.append(n)

Examples

python
# DFS via recursion; the call stack does the ordering implicitly.
def dfs(graph, v, visited=None):
    if visited is None:
        visited = set()
    visited.add(v)
    for n in graph[v]:
        if n not in visited:
            dfs(graph, n, visited)
    return visited

Only BFS guarantees shortest path in an unweighted graph

DFS can and often does reach a target via a much longer path before a shorter one, its structure has no bias toward proximity at all. Reach for BFS specifically when "shortest path" or "closest first" is the actual requirement.

Visual Diagram

Common Mistakes

  • Using DFS for a shortest-path problem, assuming any traversal that reaches the target is good enough, when only BFS's level-order guarantees the shortest path in an unweighted graph.
  • Implementing DFS recursively on a very deep or very large graph and hitting a stack-depth limit, when an iterative, explicit-stack version would have avoided it.
  • Forgetting to track visited vertices in either traversal, causing infinite loops on graphs with cycles.
  • Assuming BFS and DFS have meaningfully different asymptotic complexity; both are O(V + E) for an adjacency-list graph, the difference is traversal order, not big-O cost.

Performance

  • Both BFS and DFS run in O(V + E) time on an adjacency-list representation, visiting every vertex and edge once; neither is inherently "faster" than the other.
  • Recursive DFS consumes call-stack space proportional to the deepest path explored, a real, practical constraint on very deep graphs that an explicit-stack iterative version sidesteps.

Best Practices

  • Choose BFS specifically when the shortest path (in hops, for an unweighted graph) or a strict level-order traversal is the actual requirement.
  • Choose DFS for backtracking-style problems, cycle detection, topological sorting, or exhaustive path exploration where traversal order along one branch matters more than proximity.
  • Prefer an iterative, explicit-stack DFS over recursion when the graph could be deep enough to risk a stack-depth limit.
  • Always track visited vertices explicitly in both traversals to avoid infinite loops on cyclic graphs.

Interview questions

What is the defining structural difference between BFS and DFS, and what data structure does each rely on?

Breadth-first search considers every neighbor of a vertex before considering any of those neighbors' own outgoing edges, per NIST's definition, extremes are searched last, which is implemented with a queue: vertices are processed in the order they were discovered, level by level outward from the start. Depth-first search does the opposite, it considers a vertex's outgoing edges (children) before any of the vertex's siblings, plunging as deep as possible along one path before backtracking, and NIST notes it's "easily implemented with recursion," which uses the call stack implicitly as its ordering structure (or an explicit stack in an iterative version).

Why does BFS guarantee the shortest path in an unweighted graph, while DFS gives no such guarantee at all?

Because BFS processes vertices in strict order of distance from the start (via its queue, level by level), the first time it reaches any given vertex is necessarily via a shortest path to it, there's no way to discover a vertex at distance k before every vertex at distance k-1 has already been discovered. DFS has no such ordering property, it commits to going as deep as possible down one path before backtracking, so it can easily reach a target vertex via a long, winding path long before it would have found a much shorter one, there's nothing in DFS's structure that favors shorter paths over longer ones at all.

For the same graph, why might you choose DFS over BFS even though BFS finds shortest paths and DFS doesn't?

Shortest-path guarantees aren't always the goal, DFS is a natural fit for exhaustively exploring all possibilities along one path before trying another (backtracking problems, detecting cycles, topological sorting, finding connected components), where the actual requirement is "visit everything reachable" or "explore this branch fully before trying the next," not "find the closest thing first." DFS via recursion is also often simpler to implement for these problems, at the cost of consuming call-stack depth proportional to how deep the graph goes, which matters for very deep or very large graphs where an iterative approach (or BFS) avoids the recursion-depth risk entirely.

References

ShareXLinkedInReddit
Was this page helpful?
Suggest an improvement