Shortest Path in Unweighted Graph Using BFS in DSA Rust - Time & Space Complexity
We want to understand how the time needed to find the shortest path in an unweighted graph grows as the graph gets bigger.
Specifically, how the search using BFS behaves when the graph size changes.
Analyze the time complexity of the following code snippet.
fn bfs_shortest_path(graph: &Vec<Vec<usize>>, start: usize, target: usize) -> Option<Vec<usize>> {
let mut queue = std::collections::VecDeque::new();
let mut visited = vec![false; graph.len()];
let mut parent = vec![None; graph.len()];
visited[start] = true;
queue.push_back(start);
while let Some(node) = queue.pop_front() {
if node == target {
let mut path = Vec::new();
let mut current = Some(node);
while let Some(c) = current {
path.push(c);
current = parent[c];
}
path.reverse();
return Some(path);
}
for &neighbor in &graph[node] {
if !visited[neighbor] {
visited[neighbor] = true;
parent[neighbor] = Some(node);
queue.push_back(neighbor);
}
}
}
None
}
This code finds the shortest path between two nodes in an unweighted graph using Breadth-First Search (BFS).
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Visiting each node and checking its neighbors in the BFS loop.
- How many times: Each node is visited once, and each edge is checked once during the traversal.
As the graph grows, the BFS visits more nodes and edges, so the work grows with the total nodes and edges.
| Input Size (n nodes, e edges) | Approx. Operations |
|---|---|
| 10 nodes, 15 edges | About 25 checks (nodes + edges) |
| 100 nodes, 200 edges | About 300 checks |
| 1000 nodes, 5000 edges | About 6000 checks |
Pattern observation: The operations grow roughly in proportion to the number of nodes plus edges.
Time Complexity: O(n + e)
This means the time grows linearly with the total number of nodes and edges in the graph.
[X] Wrong: "BFS always takes O(n²) time because it uses nested loops."
[OK] Correct: BFS visits each node and edge only once, so it does not do nested work for every pair of nodes. The loops are over neighbors, not all nodes.
Understanding BFS time complexity helps you explain how graph searches scale, a key skill for many coding challenges and real-world problems.
"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, visiting all neighbors before moving deeper.Step 2: BFS finds shortest paths in unweighted graphs
Because BFS visits nodes in order of increasing distance (edges), it finds the shortest path in terms of edge count.Final Answer:
The shortest path in terms of number of edges to all reachable nodes -> Option DQuick Check:
BFS = shortest path in unweighted graph [OK]
- Confusing BFS with DFS for shortest path
- Thinking BFS finds longest paths
- Assuming BFS works for weighted graphs without modification
Solution
Step 1: Identify the correct queue type for BFS
BFS requires a FIFO queue; VecDeque in Rust supports efficient push/pop from both ends.Step 2: Check each option's suitability
let mut queue = std::collections::VecDeque::new(); uses VecDeque which is correct; Vec (A) is not efficient as a queue; HashMap (C) is a map, not a queue; BinaryHeap (D) is a priority queue, not FIFO.Final Answer:
let mut queue = std::collections::VecDeque::new(); -> Option BQuick Check:
VecDeque = correct BFS queue [OK]
- Using Vec instead of VecDeque for queue
- Using HashMap or BinaryHeap incorrectly
- Not mutably declaring the queue
let graph = vec![
vec![1, 2],
vec![0, 3],
vec![0, 3],
vec![1, 2, 4],
vec![3]
];
let start = 0;
let mut dist = vec![-1; 5];
dist[start] = 0;
let mut queue = std::collections::VecDeque::new();
queue.push_back(start);
while let Some(u) = queue.pop_front() {
for &v in &graph[u] {
if dist[v] == -1 {
dist[v] = dist[u] + 1;
queue.push_back(v);
}
}
}
println!("{:?}", dist);Solution
Step 1: Trace BFS distances from start node 0
Start at node 0 (distance 0). Neighbors 1 and 2 get distance 1. Then from 1 and 2, node 3 gets distance 2. From 3, node 4 gets distance 3.Step 2: Confirm final distance vector
Distances: node0=0, node1=1, node2=1, node3=2, node4=3.Final Answer:
0, 1, 1, 2, 3 -> Option AQuick Check:
BFS distances = [0,1,1,2,3] [OK]
- Mixing up node indices and distances
- Assigning wrong distances to nodes visited later
- Forgetting to initialize distances with -1
let graph = vec![vec![1], vec![0, 2], vec![1]];
let start = 0;
let mut dist = vec![0; 3];
let mut queue = std::collections::VecDeque::new();
queue.push_back(start);
while let Some(u) = queue.pop_front() {
for &v in &graph[u] {
if dist[v] == 0 {
dist[v] = dist[u] + 1;
queue.push_back(v);
}
}
}
println!("{:?}", dist);Solution
Step 1: Check distance vector initialization
Distances initialized to 0 means unvisited nodes and start node share same value, causing logic errors.Step 2: Explain impact on BFS logic
Condition 'if dist[v] == 0' fails to distinguish unvisited nodes, so some nodes never get updated or cause infinite loops.Final Answer:
Distance vector initialized with zeros causes confusion with unvisited nodes -> Option CQuick Check:
Use -1 for unvisited distances [OK]
- Using 0 as initial distance for all nodes
- Not marking start node distance before BFS
- Ignoring mutability of queue
0 - 1, 0 - 2, 1 - 3, 2 - 3, 3 - 4, 4 - 5Which BFS distance array after traversal correctly shows the shortest distance to each node from 0? Assume nodes are 0 to 5 and unreachable nodes have distance -1.
Solution
Step 1: Map shortest paths from node 0
From 0: nodes 1 and 2 are 1 edge away; node 3 is 2 edges away (via 1 or 2); node 4 is 3 edges away; node 5 is 4 edges away.Step 2: Confirm distances array
Distances: 0=0, 1=1, 2=1, 3=2, 4=3, 5=4 matches [0, 1, 1, 2, 3, 4].Final Answer:
[0, 1, 1, 2, 3, 4] -> Option AQuick Check:
Shortest path distances = [0,1,1,2,3,4] [OK]
- Counting nodes instead of edges
- Mislabeling unreachable nodes
- Confusing path length with node values
Ready to prove it?
Pass the test and this pattern moves unknown
