Skip to content
Forge Learn/Graph Fundamentals
Browsing as a guest. Sign in to save your progress and earn XP as you complete chapters.

BFS & DFS

6 min read

You'll learn to

  • -Implement bfs(graph, start) using a queue, and explain why it finds shortest paths in unweighted graphs
  • -Implement dfs(graph, start) using recursion, and explain why it explores depth-first
  • -Trace both traversals over the same small graph and compare their visit orders
  • -Avoid Python's recursion limit with an explicit stack, and choose between BFS and DFS for a problem

Given a graph and a starting node, there are two fundamentally different orders in which you can visit every reachable node: breadth-first (spread outward one "ring" of distance at a time) and depth-first (commit to one path and follow it as far as possible before backtracking). Both are complete. Both eventually visit every reachable node. But the order they visit nodes in, and the guarantees that order gives you, are very different.

An Analogy: Ripples and a Ball of String

Drop a stone in a pond and the ripples spread outward in rings: everything one ring away is reached before anything two rings away. That is breadth-first search. Now picture exploring a maze with a ball of string. You walk down one corridor as far as it goes, and only when you hit a dead end do you wind the string back to the last junction and try another turn. That is depth-first search.

  • -A ring of the ripple is a level: all the nodes at the same distance from the start.
  • -The order the ripples reach nodes is the queue: first discovered, first visited.
  • -The ball of string is the call stack (or an explicit stack): it remembers the way back to the last junction.

BFS: Level by Level

Breadth-first search uses a queue (FIFO, first in, first out) to guarantee that every node at distance k from the start is visited before any node at distance k + 1. The start node is enqueued first. Each time a node is dequeued, its unvisited neighbors are marked visited and enqueued. Because the queue preserves discovery order, and every neighbor of a distance-k node is at distance k or k+1, the traversal naturally exhausts one full "ring" of distance before touching the next.

bfs(graph, start). A queue enforces level-by-level order.

DFS: Go Deep First

Depth-first search instead commits to a neighbor immediately, recursing (or pushing onto an explicit stack) into that neighbor's subtree before ever returning to try a sibling neighbor. That "go all the way down one path before backing up" behavior is what recursion naturally gives you for free. Each recursive call is itself a full depth-first exploration of everything reachable from that node.

dfs(graph, start). Recursion naturally explores one branch to completion before the next.

One practical catch: the recursive version uses one call-stack frame per level of depth, and Python allows about 1,000 by default. On a graph shaped like a long path of 2,000 nodes, dfs(graph, start) raises RecursionError. The fix is to make the stack explicit. Pushing neighbors in reversed order makes it visit them in the same order as the recursive version:

dfs_iterative: the same traversal with an explicit stack, which has no depth limit

A Worked Trace

Take the small undirected square graph from the previous chapter: A connects to B and C, B connects to A and D, C connects to A and D, and D connects to B and C. First BFS from A:

  • -Dequeue A. Its unvisited neighbors B and C are marked and enqueued.
  • -Dequeue B. Its only unvisited neighbor is D, so D is enqueued.
  • -Dequeue C. D is already visited, so nothing is enqueued.
  • -Dequeue D. The queue is empty. Visit order: A, B, C, D.

Now DFS from A:

  • -Visit A, and recurse at once into its first neighbor B.
  • -From B, the first unvisited neighbor is D, so recurse into D immediately, leaving C for later.
  • -From D, the only unvisited neighbor is C, so visit C last. Visit order: A, B, D, C.

Notice that C is one hop from A, yet DFS reaches it after D, which is two hops away, because DFS commits to a path instead of expanding evenly. BFS finishes both of A's neighbors before it goes any further. Step through both traversals in the demo below and watch how the queue and the stack differ.

BFS vs. DFS on the Same Graph

Same 8 nodes, same starting point. Watch how a queue explores level by level while a stack dives deep first.

ABCDEFGH
current in queue visited

Start at A. Mark it visited and add it to the back of the queue.

Queue: [A]

step 0 / 16
  • -BFS: queue-based, visits nodes in increasing order of distance from start, and is the standard way to find shortest paths in unweighted graphs.
  • -DFS: recursion/stack-based, dives down one path fully before backtracking, useful for cycle detection, connectivity checks, and exploring all paths.
  • -Both run in O(n + e) time and O(n) space, visiting every node and edge at most once. The difference is purely visit order, not asymptotic cost.

If a problem asks for the shortest path (fewest hops) in an unweighted graph, reach for BFS. DFS can reach the target via a much longer path first and has no built-in guarantee of finding the shortest one.

Interview Signal is part of Pro

See a real weak answer next to a real strong one for this exact topic.

Quiz is part of Pro

Test what you just read with a short quiz, and bank the XP.

Ready to Build This?

The Graph Representations & BFS/DFS level asks you to implement bfs(graph, start) and dfs(graph, start) over an adjacency-list dict, exactly as built in this chapter.

Discussion0

Join the Discussion

Sign in to leave comments, reply to others, or like insights.

Sign In to ScaleDojo

No comments yet. Be the first to start the thread!