Real-world and algorithmic applications of queues — BFS for shortest paths in unweighted graphs, level-order tree traversal, 01-BFS for binary-weighted graphs, topological sort using BFS (Kahn
Introduction
The queue's FIFO ordering makes it the natural choice for any algorithm that processes items in the order they were discovered. The most important application is Breadth-First Search, but queues power many other fundamental algorithms.
Application 2 — BFS with Distance Tracking
def bfs_distances(graph, source):
"""
Find shortest distance from source to all reachable nodes.
Distance = number of edges (unweighted).
"""
distance = {source: 0}
queue = deque([source])
while queue:
node = queue.popleft()
for neighbor in graph.get(node, []):
if neighbor not in distance:
distance[neighbor] = distance[node] + 1
queue.append(neighbor)
return distance
Application 3 — Level-Order Tree Traversal
BFS on a tree naturally produces a level-by-level traversal:
from collections import deque
def level_order_traversal(root):
"""
Return all nodes level by level.
[[level 0 nodes], [level 1 nodes], [level 2 nodes], ...]
Time: O(n), Space: O(n) (queue holds at most one level at a time)
"""
if not root:
return []
result = []
queue = deque([root])
while queue:
level_size = len(queue) # Number of nodes in current level
current_level = []
for _ in range(level_size):
node = queue.popleft()
current_level.append(node.val)
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
result.append(current_level)
return result
# Variations:
def right_side_view(root):
"""Return the rightmost node at each level."""
if not root: return []
result = []
queue = deque([root])
while queue:
level_size = len(queue)
for i in range(level_size):
node = queue.popleft()
if i == level_size - 1: # Last node in level = rightmost
result.append(node.val)
if node.left: queue.append(node.left)
if node.right: queue.append(node.right)
return result
def max_depth_bfs(root):
"""Find tree depth using BFS (count levels)."""
if not root: return 0
depth = 0
queue = deque([root])
while queue:
depth += 1
for _ in range(len(queue)):
node = queue.popleft()
if node.left: queue.append(node.left)
if node.right: queue.append(node.right)
return depth
Application 4 — Multi-Source BFS
Sometimes you want BFS starting from multiple sources simultaneously:
def walls_and_gates(rooms):
"""
Given a 2D grid where:
INF = empty room
-1 = wall
0 = gate
Fill each empty room with the distance to its nearest gate.
Multi-source BFS: start BFS from all gates simultaneously.
Time: O(m × n), Space: O(m × n)
"""
INF = float('inf')
m, n = len(rooms), len(rooms[0])
queue = deque()
# Initialize: enqueue all gates
for i in range(m):
for j in range(n):
if rooms[i][j] == 0:
queue.append((i, j))
# BFS from all gates simultaneously
directions = [(0,1),(0,-1),(1,0),(-1,0)]
while queue:
row, col = queue.popleft()
for dr, dc in directions:
r, c = row + dr, col + dc
if 0 <= r < m and 0 <= c < n and rooms[r][c] == INF:
rooms[r][c] = rooms[row][col] + 1 # Distance = parent + 1
queue.append((r, c))
# Example:
rooms = [
[INF, -1, 0, INF],
[INF, INF, INF, -1],
[INF, -1, INF, -1],
[0, -1, INF, INF]
]
# After: 3 -1 0 1 / 2 2 1 -1 / 1 -1 2 -1 / 0 -1 3 4
Application 5 — 0-1 BFS (Binary Weighted Graph)
When edges have weight 0 or 1, use a deque instead of a priority queue for O(V + E) vs O((V+E) log V):
from collections import deque
def zero_one_bfs(graph, source, n):
"""
Shortest path in graph with edge weights 0 or 1.
Weight-0 edges: prepend to front (like staying at same level)
Weight-1 edges: append to rear (like going to next level)
Time: O(V + E), Space: O(V)
"""
dist = [float('inf')] * n
dist[source] = 0
dq = deque([source])
while dq:
node = dq.popleft()
for neighbor, weight in graph[node]:
if dist[node] + weight < dist[neighbor]:
dist[neighbor] = dist[node] + weight
if weight == 0:
dq.appendleft(neighbor) # Weight 0: front
else:
dq.append(neighbor) # Weight 1: rear
return dist
Application 6 — Topological Sort (Kahn's Algorithm)
BFS-based topological sort using in-degree counting:
from collections import deque
def topological_sort_kahn(graph, n):
"""
Kahn's algorithm: repeatedly remove nodes with no incoming edges.
Time: O(V + E), Space: O(V)
Returns: topological order, or [] if cycle exists.
"""
# Count in-degrees
in_degree = [0] * n
for node in range(n):
for neighbor in graph.get(node, []):
in_degree[neighbor] += 1
# Start with all nodes having in-degree 0
queue = deque([i for i in range(n) if in_degree[i] == 0])
order = []
while queue:
node = queue.popleft()
order.append(node)
for neighbor in graph.get(node, []):
in_degree[neighbor] -= 1
if in_degree[neighbor] == 0:
queue.append(neighbor)
# If order doesn't include all nodes, there's a cycle
return order if len(order) == n else []
# Course prerequisites:
graph = {0: [1,2], 1: [3], 2: [3], 3: [4], 4: []}
print(topological_sort_kahn(graph, 5)) # [0, 1, 2, 3, 4] or similar valid order
Application 7 — BFS on 2D Grid (Island Problems)
def num_islands(grid):
"""
Count number of islands (connected regions of '1').
BFS: each discovered '1' triggers BFS to mark entire island.
Time: O(m × n), Space: O(min(m, n)) — BFS queue
"""
if not grid: return 0
m, n = len(grid), len(grid[0])
count = 0
def bfs(row, col):
queue = deque([(row, col)])
grid[row][col] = '0' # Mark visited
while queue:
r, c = queue.popleft()
for dr, dc in [(0,1),(0,-1),(1,0),(-1,0)]:
nr, nc = r + dr, c + dc
if 0 <= nr < m and 0 <= nc < n and grid[nr][nc] == '1':
grid[nr][nc] = '0'
queue.append((nr, nc))
for i in range(m):
for j in range(n):
if grid[i][j] == '1':
bfs(i, j)
count += 1
return count
Summary of Queue Applications
| Algorithm | Queue Type | Time | Key Property Used |
|---|
| BFS shortest path | Regular queue | O(V+E) | FIFO = level-by-level |
| Level-order traversal | Regular queue | O(n) | Process one level at a time |
| Multi-source BFS | Regular queue | O(m×n) | Start from all sources |
| 0-1 BFS | Deque | O(V+E) | Prepend 0-weight, append 1-weight |
| Kahn's topological sort | Regular queue | O(V+E) | Process 0-in-degree first |
| Island counting | Regular queue | O(m×n) | Explore connected region |
| Sliding window max | Monotonic deque | O(n) | Monotonic decreasing front |
| Dijkstra's algorithm | Priority queue | O((V+E) log V) | Process minimum-distance |
The unifying principle: Queues process items in discovery order (FIFO), which naturally corresponds to level-by-level or shortest-path-first exploration.
*Next Lesson: Queue Interview Problems*