Shortest Path in Unweighted Graph Using BFS in DSA Python - Time & Space Complexity
We want to understand how long it takes to find the shortest path in an unweighted graph using BFS.
Specifically, how the time grows as the graph gets bigger.
Analyze the time complexity of the following code snippet.
from collections import deque
def shortest_path_bfs(graph, start, end):
queue = deque([start])
visited = {start}
distance = {start: 0}
while queue:
node = queue.popleft()
if node == end:
return distance[node]
for neighbor in graph[node]:
if neighbor not in visited:
visited.add(neighbor)
distance[neighbor] = distance[node] + 1
queue.append(neighbor)
return -1
This code finds the shortest path length between two nodes in an unweighted graph using BFS.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Visiting each node and checking its neighbors.
- How many times: Each node is visited once, and each edge is checked once.
As the graph grows, the time to visit nodes and edges grows roughly with the number of nodes plus edges.
| Input Size (n nodes, m edges) | Approx. Operations |
|---|---|
| 10 nodes, 15 edges | About 25 operations (10 nodes + 15 edges) |
| 100 nodes, 200 edges | About 300 operations (100 + 200) |
| 1000 nodes, 5000 edges | About 6000 operations (1000 + 5000) |
Pattern observation: The work grows linearly with the number of nodes and edges combined.
Time Complexity: O(n + m)
This means the time grows linearly with the total number of nodes and edges in the graph.
[X] Wrong: "BFS takes O(n²) time because it uses nested loops."
[OK] Correct: BFS visits each node and edge only once, so it does not repeatedly check all pairs, avoiding quadratic time.
Understanding BFS time complexity helps you explain how graph searches scale and why BFS is efficient for shortest paths in unweighted graphs.
"What if the graph was represented as an adjacency matrix instead of adjacency lists? How would the time complexity change?"
Practice
Solution
Step 1: Understand BFS traversal
BFS explores nodes level by level starting from the source node.Step 2: BFS finds shortest paths in unweighted graphs
Because BFS visits nodes in order of distance, it finds the shortest path to each node.Final Answer:
The shortest path from a start node to all other nodes -> Option AQuick Check:
BFS = Shortest path in unweighted graph [OK]
- Confusing BFS with DFS for shortest path
- Thinking BFS finds longest path
- Assuming BFS works only for weighted graphs
Solution
Step 1: Identify queue operations in BFS
BFS requires adding to the back and removing from the front efficiently.Step 2: Choose data structure with O(1) pops from front
collections.deque supports fast append and popleft operations, unlike list pop(0) which is slow.Final Answer:
collections.deque for efficient pops from front -> Option BQuick Check:
Use deque for BFS queue [OK]
- Using list pop(0) causing slow performance
- Using set which is unordered
- Using dict which is not a queue
graph = {0: [1, 2], 1: [0, 3], 2: [0], 3: [1]}Solution
Step 1: Initialize distances and start BFS from node 0
Distance to 0 is 0, others are infinity initially.Step 2: Visit neighbors level-wise
Neighbors of 0 are 1 and 2, distance 1 each. Then neighbor of 1 is 3, distance 2.Final Answer:
[0, 1, 1, 2] -> Option CQuick Check:
Distances = [0,1,1,2] [OK]
- Mixing node indices with distances
- Assigning wrong distances to neighbors
- Forgetting to update distances only once
def bfs(graph, start):
from collections import deque
queue = deque([start])
distances = {start: 0}
while queue:
node = queue.pop()
for neighbor in graph[node]:
if neighbor not in distances:
distances[neighbor] = distances[node] + 1
queue.append(neighbor)
return distancesSolution
Step 1: Check queue operations in BFS
BFS requires removing from front (FIFO), but pop() removes from end (LIFO).Step 2: Identify correct method to remove front element
queue.popleft() removes from front, preserving BFS order.Final Answer:
Using queue.pop() instead of queue.popleft() causes wrong order -> Option AQuick Check:
Use popleft() for BFS queue [OK]
- Using pop() causing DFS-like behavior
- Confusing list and dict for distances
- Incorrect initialization of distances
grid = [ [0, 0, 1], [1, 0, 1], [1, 0, 0] ]
Solution
Step 1: Model grid as graph nodes and edges
Each open cell is a node; edges connect up/down/left/right neighbors if open.Step 2: Run BFS from top-left to bottom-right
BFS finds shortest path in unweighted graph; blocked cells are excluded from edges.Final Answer:
Treat each cell as a node, edges connect adjacent open cells; run BFS from start to find shortest path -> Option DQuick Check:
Grid BFS = shortest path via open neighbors [OK]
- Using DFS which is not shortest path
- Ignoring blocked cells incorrectly
- Not connecting neighbors properly
Ready to prove it?
Pass the test and this pattern moves unknown
