Working Principle
Breadth-First Search explores a graph outward in layers: first the source vertex, then every neighbor at distance 1, then every unvisited vertex at distance 2, and so on. It achieves this using a queue (FIFO): enqueue the source, then repeatedly dequeue a vertex, mark it visited, and enqueue every unvisited neighbor.
Because a queue always processes vertices in the order they were discovered, and because a vertex is only ever enqueued the first time it's reached, BFS guarantees that a vertex at true graph distance \(d\) from the source is dequeued only after every vertex at distance \(< d\) has already been dequeued. This "layer-by-layer" guarantee is the entire reason BFS solves shortest paths in unweighted graphs.
Key Insight
BFS and DFS (the next deep dive) differ only in the data structure holding the frontier: BFS uses a queue and explores broadly; DFS uses a stack (or recursion, which is an implicit stack) and dives deep. Every other line of the algorithm is nearly identical — which is why the two are almost always taught, and implemented, side by side.
def bfs_pseudocode(graph, source):
"""
Precondition: `graph` maps each vertex to a list of neighbors.
Postcondition: dist[v] = shortest number of edges from source to v
(or None if v is unreachable).
"""
from collections import deque
dist = {source: 0}
queue = deque([source])
while queue:
u = queue.popleft() # FIFO: earliest-discovered vertex first
for v in graph[u]:
if v not in dist: # first time v is ever discovered
dist[v] = dist[u] + 1
queue.append(v)
return dist
Interactive Demo
Step through BFS (then DFS, in the next deep dive) on the same 6-node graph. Watch how the queue processes nodes strictly in discovery order.
BFS vs DFS — Live Comparison
Click Next to step through BFS first, then DFS on the same graph.
Complexity Analysis
Every vertex is enqueued at most once (guarded by the "first time discovered" check), so the outer loop runs \(O(V)\) times. Every edge is examined at most twice in an undirected graph (once from each endpoint) or once in a directed graph, so the total work across all iterations of the inner loop is \(O(E)\). Combined:
$$\text{Time: } O(V + E) \qquad \text{Space: } O(V) \text{ for the visited/distance map and the queue}$$
Why O(V+E) and Not O(V) or O(E) Alone?
Neither term alone bounds the work: a graph can have \(V\) vertices but \(0\) edges (isolated vertices still cost \(O(1)\) each to visit), or very few vertices but many edges (a dense graph). \(O(V+E)\) — sometimes called linear in the size of the graph — is the tight bound that accounts for both costs honestly.
Implementation
Correctness Proof Sketch
The proof is a direct application of the induction technique from Part 1. Claim: when BFS finishes, \(\text{dist}[v]\) equals the true shortest-path distance (in edges) from the source to \(v\), for every reachable \(v\).
Loop invariant: at every point during execution, the queue contains vertices from at most two consecutive distance layers, ordered so that all layer-\(k\) vertices precede all layer-\((k+1)\) vertices. Base case: initially the queue holds only the source at layer 0. Inductive step: when a layer-\(k\) vertex is dequeued and processed, any newly discovered neighbor is assigned distance \(k+1\) and appended — preserving the ordering. Since the queue is strictly FIFO, no layer-\((k+1)\) vertex is ever dequeued before all layer-\(k\) vertices are dequeued, and a vertex's distance is only ever set once (on first discovery) — so it can never be set to anything other than the true minimum.
Real-World Applications
Social-Network "Degrees of Separation"
LinkedIn's "2nd-degree connection" and "3rd-degree connection" labels are literally BFS distance-1 and distance-2 (and beyond) layers computed from your profile as the source vertex. The same layer-by-layer structure powers friend-of-friend recommendation systems and the classic "six degrees of separation" experiments.
Other standard uses: unweighted shortest paths, testing whether a graph is bipartite (2-color each BFS layer alternately and check for same-layer edges), finding connected components, and web-crawler frontier management (crawl breadth-first from seed URLs).
Limitations
BFS Does Not Handle Weighted Edges
BFS's shortest-path guarantee relies entirely on every edge counting as exactly "1 step." The moment edges have different weights, a path with more edges can be cheaper than a path with fewer — BFS will get this wrong. That's exactly the gap Dijkstra's algorithm fills.