Shortest Path in DAG Using Topological Order in DSA Rust - Time & Space Complexity
We want to understand how the time needed to find the shortest path in a Directed Acyclic Graph (DAG) grows as the graph gets bigger.
Specifically, how the steps increase when using topological order to find shortest paths.
Analyze the time complexity of the following code snippet.
fn shortest_path_dag(graph: &Vec>, start: usize) -> Vec {
let n = graph.len();
let mut dist = vec![i32::MAX; n];
dist[start] = 0;
let order = topological_sort(graph);
for &u in &order {
if dist[u] != i32::MAX {
for &(v, w) in &graph[u] {
if dist[u] + w < dist[v] {
dist[v] = dist[u] + w;
}
}
}
}
dist
}
This code finds the shortest path distances from a start node to all others in a DAG using topological order.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: The nested loops: outer loop over all nodes in topological order, inner loop over all edges from each node.
- How many times: Outer loop runs once per node (n times), inner loop runs once per outgoing edge (total m times across all nodes).
As the graph grows, the number of nodes (n) and edges (m) increase, and the code checks each edge once.
| Input Size (n, m) | Approx. Operations |
|---|---|
| 10 nodes, 15 edges | About 25 steps (10 + 15) |
| 100 nodes, 200 edges | About 300 steps (100 + 200) |
| 1000 nodes, 5000 edges | About 6000 steps (1000 + 5000) |
Pattern observation: The operations grow roughly in proportion to the sum of nodes and edges.
Time Complexity: O(n + m)
This means the time to find shortest paths grows linearly with the number of nodes plus the number of edges in the graph.
[X] Wrong: "The algorithm takes O(n²) time because it has nested loops."
[OK] Correct: The inner loop does not run n times for each node; it runs once per edge overall, so total work depends on edges, not n squared.
Understanding this time complexity helps you explain efficient graph algorithms clearly and confidently in interviews.
"What if the graph was not a DAG and contained cycles? How would the time complexity and approach change?"
Practice
Solution
Step 1: Understand topological order in DAG
Topological order arranges vertices so all edges go from earlier to later vertices, ensuring no cycles.Step 2: Use topological order for shortest path
Processing vertices in this order allows updating shortest paths without revisiting nodes, making the algorithm efficient.Final Answer:
It allows processing vertices in a linear order without revisiting nodes. -> Option DQuick Check:
Topological order = linear processing [OK]
- Thinking DAG has cycles
- Confusing topological order with sorting by weights
- Assuming it finds longest path
Solution
Step 1: Initialize distances with maximum value
Using vec![i32::MAX; n] sets all distances to infinity (max int), representing unknown paths.Step 2: Set source distance to zero
dist[0] = 0 sets the source vertex distance to zero, as it is the start point.Final Answer:
let dist = vec![i32::MAX; n]; dist[0] = 0; -> Option AQuick Check:
Initialize with max, source zero [OK]
- Setting source distance to max instead of zero
- Using zero for all distances initially
- Using Option types unnecessarily
let n = 4;
let edges = vec![
(0, 1, 1),
(0, 2, 4),
(1, 2, 2),
(1, 3, 6),
(2, 3, 3),
];
let mut dist = vec![i32::MAX; n];
dist[0] = 0;
let topo_order = vec![0, 1, 2, 3];
for &u in &topo_order {
for &(src, dst, w) in &edges {
if src == u && dist[u] != i32::MAX && dist[u] + w < dist[dst] {
dist[dst] = dist[u] + w;
}
}
}
println!("{:?}", dist);Solution
Step 1: Initialize distances and process vertex 0
dist = [0, MAX, MAX, MAX]. From 0: update dist[1] = 0+1=1, dist[2] = 0+4=4.Step 2: Process vertex 1 and 2
From 1: dist[2] = min(4, 1+2=3) = 3, dist[3] = 1+6=7. From 2: dist[3] = min(7, 3+3=6) = 6.Step 3: Process vertex 3
No outgoing edges, distances remain.Final Answer:
[0, 1, 3, 6] -> Option BQuick Check:
Relax edges in topo order = [0,1,3,6] [OK]
- Not updating dist[2] to 3 via vertex 1
- Ignoring better path to dist[3]
- Printing distances before updates
let n = 3;
let edges = vec![(0, 1, 5), (1, 2, 10)];
let mut dist = vec![i32::MAX; n];
dist[0] = 0;
let topo_order = vec![0, 1, 2];
for u in topo_order {
for &(src, dst, w) in &edges {
if src == u && dist[dst] != i32::MAX && dist[u] + w < dist[dst] {
dist[dst] = dist[u] + w;
}
}
}
println!("{:?}", dist);Solution
Step 1: Analyze the if condition in edge relaxation
The condition 'dist[dst] != i32::MAX' wrongly blocks updating dist[dst] if it is still infinity, so no update happens initially.Step 2: Correct the condition to allow updates
Remove 'dist[dst] != i32::MAX' so that if dist[u] + w is less, dist[dst] updates even if dist[dst] was infinity.Final Answer:
The condition 'dist[dst] != i32::MAX' prevents updating distances correctly. -> Option AQuick Check:
Wrong condition blocks updates [OK]
- Adding unnecessary condition on dist[dst]
- Incorrect topological order
- Initializing distances with zeros
Solution
Step 1: Initialize distances and process vertex 0
dist = [0, MAX, MAX, MAX, MAX]. From 0: dist[1] = 2, dist[2] = 3.Step 2: Process vertex 1 and 2
From 1: dist[3] = 2 + 4 = 6. From 2: dist[3] = min(6, 3 + 1 = 4) = 4.Step 3: Process vertex 3 and 4
From 3: dist[4] = 4 + 5 = 9. Vertex 4 has no outgoing edges.Final Answer:
9 -> Option CQuick Check:
Shortest path 0->2->3->4 = 9 [OK]
- Choosing path 0->1->3->4 without checking shorter 0->2->3->4
- Adding weights incorrectly
- Ignoring topological order
Ready to prove it?
Pass the test and this pattern moves unknown
