Yasir Explains/Algorithms/Graphs and Basic Traversal Techniques/Breadth-First Search (BFS)
Graphs and Basic Traversal Techniques

Breadth-First Search (BFS)

On this page

The idea — explore in layersThree ingredients: queue, visited, distanceStep-by-step visualizationWhy BFS finds shortest paths (in unweighted graphs)PseudocodeComplexity analysis
Graphs and Basic Traversal Techniques

Breadth-First Search (BFS)

Explore a graph level by level using a queue: visit the source, then all vertices one edge away, then two edges away, and so on. Runs in O(V + E) and is the foundation of unweighted-shortest-path algorithms.

The idea — explore in layers

Breadth-First Search explores a graph the way ripples spread on a pond. Starting from a source vertex s, it visits:

  1. s itself (distance 0),
  2. every neighbor of s (distance 1),
  3. every still-unvisited neighbor of those (distance 2),
  4. and so on until every vertex reachable from s has been seen.

The machinery that produces this layered order is a FIFO queue: we put s in, and every time we pop a vertex we push its unvisited neighbors. Because the queue is first-in-first-out, layer k is fully drained before any layer-k+1 vertex is processed.

This gives BFS a remarkable property:

The order in which BFS first visits a vertex equals its distance (in number of edges) from the source.

That is why BFS finds shortest paths in unweighted graphs.

Three ingredients: queue, visited, distance

Every BFS implementation carries three pieces of state:

  • A queue Q — vertices discovered but not yet processed.
  • A visited array (boolean) — has this vertex ever been enqueued? Prevents revisiting and infinite loops in graphs with cycles.
  • A dist array (integer) — the BFS distance from the source. Initialized to ∞ (or -1); set to dist[u] + 1 the first time a vertex is discovered.

The invariant: a vertex is enqueued exactly once. The first time you see a vertex is the only time you see it, because BFS guarantees that first arrival is via a shortest path.

Optionally also store parent — the vertex that discovered this one — so you can reconstruct the actual path back to the source.

Step-by-step visualization

Run BFS from source 1 on this undirected graph, one step at a time. Step forward and backward with the ← / → arrow keys or the buttons below.

Watch the three pieces of state evolve together:

  • Node colors — gray = unvisited, cyan = discovered and waiting in the queue, amber = the vertex currently being processed, green = finished.
  • The queue panel — vertices enter at the back and leave from the front (FIFO), which is exactly what produces the layer-by-layer order.
  • The dist[] labels — set once, the first time a vertex is discovered, and never changed again.

The cyan edges are tree edges — the edge along which each vertex was first discovered. Together they form the BFS tree, and every vertex's dist label is its shortest-path distance from the source.

1d=∞2d=∞5d=∞3d=∞4d=∞6d=∞
UnvisitedIn queue (discovered)Current (dequeued)Finished
Queuefront →
empty
dist[]
1
∞
2
∞
3
∞
4
∞
5
∞
6
∞
Start: every vertex is unvisited, dist = ∞, and the queue is empty.
1 / 27
Speed

Tip: step through with the ← / → arrow keys.

Why BFS finds shortest paths (in unweighted graphs)

Here is the one-paragraph argument. The queue is processed in FIFO order, so all vertices at distance k are popped before any vertex at distance k + 1. When a popped vertex u discovers a previously unvisited v, the new dist[v] = dist[u] + 1. If a shorter path to v existed (< dist[u] + 1), some vertex on that path would have popped first and would have visited v earlier — contradiction. So the first time we reach v is via a shortest path.

This is why BFS shows up everywhere:

  • Shortest path in unweighted graphs and on grids.
  • Connected components in undirected graphs — repeat BFS from each unvisited vertex.
  • Bipartite check — 2-color the BFS layers; the graph is bipartite iff no edge joins two same-colored vertices.
  • Level-order traversal of a tree.
  • Topological sort via Kahn's algorithm uses the same queue mechanism.

Pseudocode

The complete BFS routine plus shortest-path reconstruction from the parent[] array. Every line maps directly onto the visualization above: the while loop is the dequeue step, and the if not visited branch is the discover-and-enqueue step. The C++ tab has a ready-to-run program on the exact graph from the visualization.

1BFS(G, s):
2 for each vertex v in G:
3 visited[v] = false
4 dist[v] = ∞
5 parent[v] = NIL
6
7 visited[s] = true
8 dist[s] = 0
9 enqueue(Q, s)
10
11 while Q is not empty:
12 u = dequeue(Q)
13 for each neighbor v of u:
14 if not visited[v]:
15 visited[v] = true
16 dist[v] = dist[u] + 1
17 parent[v] = u
18 enqueue(Q, v)
19
20 return dist, parent
21
22// Reconstruct the path from s to t (after BFS from s):
23PATH(s, t, parent):
24 if parent[t] == NIL and t != s: return "unreachable"
25 P = empty list
26 cur = t
27 while cur != NIL:
28 prepend cur to P
29 cur = parent[cur]
30 return P

Complexity analysis

Each vertex is enqueued at most once → it is dequeued at most once → its adjacency list is scanned at most once.

Sum over all vertices: each vertex contributes O(1) for enqueue/dequeue + O(deg(v)) for scanning its neighbors. The total work for scanning is Σ deg(v) = 2E for undirected (or E for directed) — every edge is inspected a constant number of times.

  • Time: O(V + E).
  • Space: O(V) for the queue, visited, and dist arrays.

With an adjacency list, both bounds are achieved. With an adjacency matrix, neighbor iteration is O(V) per vertex, so the running time degrades to O(V²).

Complexity Analysis

Time Complexity

O(V + E) with an adjacency list; O(V²) with an adjacency matrix.

Space Complexity

O(V) for the queue, visited array, and dist/parent arrays.

BFS gives correct shortest-path distances only for unweighted graphs (or graphs where every edge has the same positive weight). For non-uniform positive weights, use Dijkstra; for mixed weights, use Bellman–Ford or SPFA.

Growth Rate Comparison

n (input size)O(1)O(log n)O(n)O(n log n)O(n²)