Find the Shortest Path in an Undirected Graph from a source to every other node when every edge costs 1. We build intuition for Breadth-First Search, walk through the code line by line, add a dry run, and finish with precise Big-O analysis.
Here’s the [Problem Link] to begin with.
1. Problem statement
You are given an undirected graph with n vertices (0 … n-1).
All edges have unit weight (cost = 1).
For a given source vertex src, return an array distance where
distance[i]= the fewest edges needed to reach vertexifromsrc,- If a vertex is unreachable, its distance is
-1.
2. Example
n = 6
Edges:
0-1, 0-2, 1-3, 2-3, 3-4| Vertex | Shortest path from 0 | Distance |
|---|---|---|
| 0 | 0 | 0 |
| 1 | 0-1 | 1 |
| 2 | 0-2 | 1 |
| 3 | 0-1-3 or 0-2-3 | 2 |
| 4 | 0-1-3-4 | 3 |
| 5 | – | -1 (not connected) |
distance = [0, 1, 1, 2, 3, -1]
3. Intuition & approach
3.1 Why Breadth-First Search (BFS)?
- Every edge costs the same (1).
- BFS explores the graph level by level:
- Level 0 = source itself.
- Level 1 = all vertices one edge away.
- Level 2 = all vertices two edges away …
- The first time BFS reaches a vertex, it has already found the shortest possible path (fewest edges).
So BFS gives us the answer in a single scan.
3.2 Algorithm steps
- Setup a queue to process vertices in order of their distance.
distance[src] = 0; push(src, 0)into the queue.- Pop a vertex
(node, distSoFar)- For every neighbour
adjNode- If the neighbour hasn’t been visited (
distance == -1)- Set
distance[adjNode] = distSoFar + 1 - Push
(adjNode, distSoFar + 1)into the queue.
- Set
- If the neighbour hasn’t been visited (
- For every neighbour
- Repeat until the queue is empty.
- Return the
distancearray.
Because each vertex enters the queue once, the algorithm runs in linear time.
You may be interested in solving Shortest Path in a Weighted DAG.
4. Python code
from collections import deque
class Solution:
def shortestPath(self, adj, src):
n = len(adj) # number of vertices
distance = [-1 for _ in range(n)] # -1 = not reached yet
queue = deque()
queue.append([src, 0]) # start from source
distance[src] = 0
while len(queue) != 0:
node, dis_trav = queue.popleft() # current vertex + distance
for adjNode in adj[node]: # explore neighbours
if distance[adjNode] == -1: # first time we see it
distance[adjNode] = dis_trav + 1 # shortest path fixed
queue.append([adjNode, dis_trav + 1])
return distance5. Step-by-step code explanation
- Lines 6-8 –
nstores the vertex count.distancearray starts at-1for every vertex.queuewill maintain vertices to process in FIFO order.
- Line 10 – Push the source
(src, 0); distance to itself is 0. - Main
whileloop (Lines 12-17)popleft()fetches the closest unprocessed vertex (node) and its current shortest distance (dis_trav).- Loop over every neighbour
adjNode:- Unvisited? (distance = -1)
- Set its distance to
dis_trav + 1. - Enqueue it with this new distance.
- Set its distance to
- Unvisited? (distance = -1)
- Each neighbour enters the queue once, ensuring O(V + E) time.
- Line 17 – When the queue empties, all reachable vertices have been assigned their minimum distances. The function returns the completed array.
6. Dry run on the earlier example graph
Queue state shown as [vertex, dist].
| Step | Queue (before pop) | distance after processing |
|---|---|---|
| Init | [[0,0]] | [0,-1,-1,-1,-1,-1] |
| Pop 0 | neighbours 1,2 → enqueue [1,1],[2,1] | [0,1,1,-1,-1,-1] |
| Pop 1 | neighbour 3 (new) → enqueue [3,2] | [0,1,1,2,-1,-1] |
| Pop 2 | neighbour 3 already visited | unchanged |
| Pop 3 | neighbour 4 → enqueue [4,3] | [0,1,1,2,3,-1] |
| Pop 4 | no new neighbour | unchanged |
| Queue empty | – | Final distances |
Matches [0, 1, 1, 2, 3, -1].
7. Complexity
| Measure | Value | Explanation |
|---|---|---|
| Time | O(V + E) | Each vertex enqueued once; each undirected edge examined twice (once per direction) |
| Space | O(V) | distance array + queue (worst-case all vertices inside) |
V = n, E = number of edges.
8. Conclusion
- Breadth-First Search is the go-to tool for shortest paths when all edges cost the same.
- A single queue and a
distancearray are enough, no fancy data structures. - Because we mark a vertex the first time we see it, its distance is already minimal.
- The solution is linear, easy to code, and scales to very large sparse graphs.
For any changes to the article, kindly email at code@codeanddebug.in or contact us at +91-9712928220.
