Worked examples — BFS — algorithm, queue-based, O(V+E), shortest path in unweighted graphs
We reuse the parent's engine exactly:
BFS(s):
dist[s]=0; visited[s]=True; queue=[s]
while queue:
u = queue.popleft() # oldest → smallest dist
for w in adj[u]:
if not visited[w]:
visited[w]=True # mark at PUSH time
dist[w]=dist[u]+1
parent[w]=u
queue.append(w)
The scenario matrix
Every graph BFS meets falls into one of these case classes. The examples below each tag which cell(s) they cover, and together they hit all of them.
| # | Case class | What's special | Covered by |
|---|---|---|---|
| A | Simple connected, unique paths | plain shortest path | Ex 1 |
| B | Two equal-length shortest paths | tie-breaking / who discovers first | Ex 2 |
| C | Cycle present | must not loop forever | Ex 3 |
| D | Disconnected graph | some nodes unreachable → | Ex 4 |
| E | Degenerate: single node / self-loop | zero edges, edge to self | Ex 5 |
| F | Directed graph | edges one-way, reverse blocked | Ex 6 |
| G | Real-world: grid maze | implicit graph, moves = edges | Ex 7 |
| H | Exam twist: multi-source BFS | many starts at distance 0 | Ex 8 |
| I | Trap: weighted graph misuse | BFS gives WRONG cost | Ex 9 |
Example 1 — Case A: simple connected, unique shortest path

Steps.
d0=0, queue[0]. Why? distance to itself is zero — the ripple's centre.- Pop
0; neighbors1,4unvisited →d1=1, d4=1, queue[1,4]. Why? both are one hop from source, so level 1. - Pop
1; neighbor2unvisited →d2=2, queue[4,2]. Why?1popped before4(FIFO), so its children get processed first, but they still get level 2. - Pop
4; neighbor3unvisited →d3=2, queue[2,3]. Why?4is at level 1, so3is level 2 — the 2-edge route wins, exactly as the ring model predicts. - Pop
2; neighbor3already visited → skip. Why? the visited check kills the longer 3-edge route before it can overwrite. - Pop
3; no new nodes. Queue empty → done.
Result: .
Recall Verify
Sanity: max distance = 2, and the graph's diameter (longest shortest path) from 0 is indeed 2. Node 3 got the fewer-edge route. ✓
Example 2 — Case B: two equal shortest paths, who discovers first?
Steps.
d0=0, queue[0].- Pop
0; scan neighbors in list order1then2→d1=1,parent1=0;d2=1,parent2=0. Queue[1,2]. Why order matters?1enters the queue before2. - Pop
1(FIFO, it's oldest); neighbor3unvisited →d3=2, parent3=1. Queue[2,3]. Why parent is 1?1reached3first because it was dequeued first. - Pop
2; neighbor3already visited → skip. Why? the tie is already resolved; the second equal path is discarded.
Result: , parent[3]=1. Path reconstructed: 3→1→0, reversed 0,1,3.
Recall Verify
Both paths length 2 ⇒ regardless of parent choice. ✓ Only the witness path changes, never the distance.
Example 3 — Case C: a cycle, don't loop forever

Steps.
d0=0, queue[0].- Pop
0; neighbors1,2unvisited →d1=1,d2=1, queue[1,2]. - Pop
1; neighbors are0(visited) and2(visited) → both skipped. Why? the cycle's back-edges hit already-visited nodes and are ignored — this is what stops the infinite loop. - Pop
2; neighbors1(visited),0(visited) skipped,3unvisited →d3=2, queue[3]. - Pop
3; no new. Done.
Result: , terminates cleanly.
Recall Verify
Each of 4 vertices dequeued exactly once → 4 pops, no more, so termination is guaranteed. ✓ Cycle handled by the visited guard.
Example 4 — Case D: disconnected graph, unreachable = infinity
Steps.
- Init all
dist=∞exceptd0=0. Why ∞? it marks "not yet reached"; unreachable nodes keep this value. - BFS from 0 reaches
0(0),1(1),2(2). Queue empties. - Nodes
3,4were never pushed → theirdiststays∞. Why? no edge crosses between components; the ripple can't jump the gap.
Result: .
Recall Verify
Component of 0 = {0,1,2}; component of 3 = {3,4}. Cross-component distance is undefined ⇒ ∞. ✓
Example 5 — Case E: degenerate inputs (single node, self-loop)
Steps — (a) single node.
d0=0, queue[0].- Pop
0;adj[0]is empty → no neighbors. Queue empties. Done. - Result: . Why fine? BFS on a lone node just labels the source and stops — the base case.
Steps — (b) self-loop 0-0 + 0-1.
d0=0, queue[0].- Pop
0; scan neighbors:0itself → already visited (marked at start) → skip.1unvisited →d1=1. Why the self-loop is harmless?0was marked visited before the loop, so the edge0→0is instantly rejected. - Result: .
Recall Verify
(a) 1 node, 1 pop, d0=0. ✓ (b) self-loop skipped, d1=1. ✓ Degenerate edges never break the visited guard.
Example 6 — Case F: directed graph, one-way edges

Steps.
d0=0, queue[0].- Pop
0; out-edges0→1, 0→3→d1=1,d3=1, queue[1,3]. Why only out-edges? in a directed graph you may only follow arrows in their pointing direction. - Pop
1; out-edge1→2→d2=2, queue[3,2]. - Pop
3; no out-edges. Pop2; out-edge2→0→0visited, skip. Done.
Result: . Reaching 2 needed the long way 0→1→2 because 0→2 isn't an arrow.
Recall Verify
2 reachable only via 0→1→2, length 2 ⇒ . ✓ Edge 2→0 and 0→3 don't shorten anything.
Example 7 — Case G: real-world grid maze (implicit graph)

We use (row,col) as node names; neighbors are computed on the fly (no adjacency list stored).
Steps.
d(0,0)=0, queue[(0,0)]. Why implicit? the graph is huge/regular — cheaper to compute the 4 neighbors than store edges.- Pop
(0,0); neighbors: right(0,1)open→d=1; down(1,0)is#→ blocked. Queue[(0,1)]. Why check walls? walls = missing edges. - Pop
(0,1); down(1,1)open →d=2; right(0,2)is#skip. Queue[(1,1)]. - Pop
(1,1); right(1,2)→d=3; down(2,1)→d=3. Queue[(1,2),(2,1)]. - Pop
(1,2); down(2,2)=G→d=4. Goal reached.
Result: minimum moves to G = 4. One shortest path: (0,0)(0,1)(1,1)(1,2)(2,2).
Recall Verify
Manhattan lower bound = |2−0|+|2−0| = 4, and BFS found exactly 4 ⇒ no detour needed, path is optimal. ✓
Example 8 — Case H: exam twist, multi-source BFS
Steps.
d0=0, d4=0, queue[0,4]. Why both at 0? every source is its own zero-distance ripple centre; they all start level 0.- Pop
0; neighbor1→d1=1, queue[4,1]. - Pop
4; neighbor3→d3=1, queue[1,3]. - Pop
1; neighbor2→d2=2, queue[3,2]. - Pop
3; neighbor2already visited → skip. Why?2was reached from source 0's side first, and both sides are equidistant, so the min is locked.
Result: — each is distance to nearest source. Node 2 is 2 from either end.
Recall Verify
. ✓ Multi-source = one BFS with a pre-loaded queue.
Example 9 — Case I: the trap, BFS on a weighted graph
Steps.
- BFS ignores weights: pop
0, neighbors1,2→d1=1, d2=1(edge counts). - BFS reports cost to 1 = 1 edge, path
0→1, implied cost 10. - True cheapest cost =
0→2→1=1 + 1 = 2, which uses 2 edges. BFS never considers it because it stops at the first (fewer-edge) arrival.
Result: BFS answer (edges) = 1 → cost 10. Correct answer needs Dijkstra's Algorithm → cost 2.
Recall Verify
BFS edge-count to 1 = 1; Dijkstra cost = min(10, 1+1) = 2. The two disagree ⇒ BFS is the wrong tool here. ✓
Recall
Which case makes BFS give a wrong answer, and why?
In a disconnected graph, what distance do unreachable nodes get?
How does BFS avoid looping forever on a cycle?
What is multi-source BFS?
In a tie between two equal shortest paths, what decides the parent?
Connections
- BFS — main note (the engine we reuse here)
- Connected Components (Case D: loop BFS over every unvisited node)
- Dijkstra's Algorithm (Case I fix: weighted shortest paths)
- 0-1 BFS (weights restricted to 0/1)
- Shortest Path Tree (the
parentarray from Case B) - Bipartite Check (BFS by level parity — another scenario)
- DFS — depth-first traversal (contrast: stack, no shortest-path guarantee)
- Graph Representations — adjacency list vs matrix (implicit grid of Case G)