BFS Pattern with Queues: Level-Order and Shortest Path
Master BFS using queues for tree level-order traversal and shortest path in unweighted graphs. Python implementations with detailed traces.
What you'll learn
- ✓The BFS algorithm and why it uses a queue
- ✓Level-order traversal of binary trees
- ✓Shortest path in unweighted graphs
- ✓BFS template you can reuse across problems
Prerequisites
- •Queue basics — see Stacks & Queues Intro
- •Graph basics — see Graphs Intro
Breadth-First Search (BFS) explores nodes level by level. A queue is the natural data structure because it processes nodes in the order they were discovered — First In, First Out.
The BFS Template
from collections import deque
def bfs(start):
queue = deque([start])
visited = {start}
while queue:
node = queue.popleft()
process(node)
for neighbor in get_neighbors(node):
if neighbor not in visited:
visited.add(neighbor)
queue.append(neighbor)
Level-Order Tree Traversal
Process all nodes at depth d before any node at depth d+1:
def level_order(root):
"""
Return list of lists, one per level.
Time: O(n), Space: O(n)
"""
if not root:
return []
result = []
queue = deque([root])
while queue:
level_size = len(queue)
level = []
for _ in range(level_size):
node = queue.popleft()
level.append(node.val)
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
result.append(level)
return result
Trace
Tree: 1
/ \
2 3
/ \ \
4 5 6
Queue states:
Start: [1] → level 0: [1]
After: [2, 3] → level 1: [2, 3]
After: [4, 5, 6] → level 2: [4, 5, 6]
Result: [[1], [2, 3], [4, 5, 6]]
Shortest Path in Unweighted Graph
BFS guarantees the shortest path (fewest edges) in an unweighted graph:
def shortest_path(graph, start, end):
"""
Find shortest path in unweighted graph.
Time: O(V + E), Space: O(V)
"""
queue = deque([(start, [start])])
visited = {start}
while queue:
node, path = queue.popleft()
if node == end:
return path
for neighbor in graph[node]:
if neighbor not in visited:
visited.add(neighbor)
queue.append((neighbor, path + [neighbor]))
return None # No path exists
BFS on a Grid
Many interview problems use BFS on a 2D grid:
def bfs_grid(grid, start_r, start_c):
"""BFS on a 2D grid."""
rows, cols = len(grid), len(grid[0])
queue = deque([(start_r, start_c, 0)]) # row, col, distance
visited = {(start_r, start_c)}
directions = [(0,1), (0,-1), (1,0), (-1,0)]
while queue:
r, c, dist = queue.popleft()
for dr, dc in directions:
nr, nc = r + dr, c + dc
if (0 <= nr < rows and 0 <= nc < cols
and (nr, nc) not in visited
and grid[nr][nc] != '#'):
visited.add((nr, nc))
queue.append((nr, nc, dist + 1))
Why Queue and Not Stack?
| Data Structure | Traversal | Finds |
|---|---|---|
| Queue (BFS) | Level by level | Shortest path |
| Stack (DFS) | Deep first | Any path |
BFS with a queue guarantees that when you first reach a node, you’ve found the shortest path to it. DFS with a stack might find a longer path first.
Edge Cases
- Empty graph/tree — return empty result
- Single node — return immediately
- Disconnected graph — BFS only visits the connected component
- Cycles — the
visitedset prevents infinite loops
When to Use BFS
- Shortest path in unweighted graph
- Level-order traversal
- Finding all nodes within k distance
- Topological sort (Kahn’s algorithm)
- Multi-source BFS (rotten oranges, walls and gates)
Related Problems
- Binary Tree Level Order (LeetCode 102)
- Shortest Path in Binary Matrix (LeetCode 1091)
- Rotten Oranges (LeetCode 994)
- Word Ladder (LeetCode 127)
Related articles
- DSA Design Circular Deque — Array-Based Implementation (LeetCode 641)
Design a Circular Deque with front/rear pointers on a fixed-size array. Python solution with all O(1) operations, visual trace, and edge case handling.
- DSA Design Hit Counter Using Queue
Design a hit counter that counts hits in the past 5 minutes using a queue. LeetCode 362 solution with O(1) amortized operations.
- DSA First Non-Repeating Character in a Stream
Find the first non-repeating character in a character stream using a queue and hash map. Python solution with O(1) amortized per query.
- DSA Implement Stack Using Two Queues — Two Approaches Explained
Implement a stack using two queues with costly push and costly pop approaches. Complete Python solutions with complexity analysis.