Shortest Path in DAG Using Topological Order in DSA Python - Time & Space Complexity
We want to understand how the time needed to find the shortest path in a Directed Acyclic Graph (DAG) changes as the graph grows.
Specifically, how the steps increase when using topological order to find shortest paths.
Analyze the time complexity of the following code snippet.
# Find shortest paths in DAG using topological order
from collections import deque
def shortest_path_dag(graph, start, n):
dist = [float('inf')] * n
dist[start] = 0
indegree = [0] * n
for u in range(n):
for v, w in graph[u]:
indegree[v] += 1
queue = deque([u for u in range(n) if indegree[u] == 0])
while queue:
u = queue.popleft()
for v, w in graph[u]:
if dist[u] + w < dist[v]:
dist[v] = dist[u] + w
indegree[v] -= 1
if indegree[v] == 0:
queue.append(v)
return dist
This code finds the shortest distance from a start node to all others in a DAG by processing nodes in topological order.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Looping over all edges in the graph to update distances.
- How many times: Each edge is visited exactly once during the while loop.
As the number of nodes and edges grow, the operations increase roughly in proportion to the sum of nodes and edges.
| Input Size (n nodes, m edges) | Approx. Operations |
|---|---|
| 10 nodes, 15 edges | About 25 operations (10 + 15) |
| 100 nodes, 200 edges | About 300 operations (100 + 200) |
| 1000 nodes, 5000 edges | About 6000 operations (1000 + 5000) |
Pattern observation: Operations grow linearly with the number of nodes plus edges.
Time Complexity: O(n + m)
This means the time needed grows linearly with the total number of nodes and edges in the graph.
[X] Wrong: "The algorithm takes O(n²) time because it looks at all pairs of nodes."
[OK] Correct: The algorithm only visits each edge once, not all pairs, so it depends on edges, not all node pairs.
Understanding this time complexity helps you explain why topological order is efficient for shortest paths in DAGs, a common interview topic.
"What if the graph had cycles? How would the time complexity and approach change?"
Practice
Solution
Step 1: Understand topological order in DAG
Topological order arranges nodes so all edges go from earlier to later nodes, ensuring no cycles.Step 2: Use topological order for shortest path
Processing nodes in this order means each node is visited once, and distances can be updated efficiently without revisiting.Final Answer:
It allows processing vertices in a linear order without revisiting nodes. -> Option AQuick Check:
Topological order = linear processing [OK]
- Thinking topological order applies to graphs with cycles
- Confusing shortest path with longest path
- Assuming topological order sorts by edge weights
Solution
Step 1: Initialize distances with infinity
We set all distances to infinity to represent unknown paths initially.Step 2: Set start node distance to zero
The distance to the start node (0) is zero because it is the source.Final Answer:
dist = [float('inf')] * 5; dist[0] = 0 -> Option DQuick Check:
Start node distance = 0, others = infinity [OK]
- Setting start node distance to infinity
- Using invalid index dist[5] (out of range)
- Initializing distances with zeros
Solution
Step 1: Calculate distances step-by-step
Start dist: [0, inf, inf, inf]. From 0: dist[1]=2, dist[2]=4. From 1: dist[2]=min(4,2+1=3)=3, dist[3]=2+7=9. From 2: dist[3]=min(9,3+3=6)=6.Step 2: Final shortest distance to node 3
The shortest distance to node 3 is 6 after updates.Final Answer:
6 -> Option AQuick Check:
Shortest path to 3 = 6 [OK]
- Not updating dist[2] to 3 via node 1
- Taking longer path 0->1->3 as shortest
- Ignoring edge weights when updating distances
dist = [float('inf')] * 4
dist[0] = 0
topo_order = [0, 1, 2, 3]
graph = {0: [(1, 1), (2, 4)], 1: [(2, 2), (3, 6)], 2: [(3, 3)], 3: []}
for u in topo_order:
for v, w in graph[u]:
if dist[u] + w < dist[v]:
dist[v] = dist[u] + w
print(dist)
What is the output?Solution
Step 1: Initialize distances and process node 0
dist = [0, inf, inf, inf]. From 0: dist[1]=1, dist[2]=4.Step 2: Process node 1 and update distances
From 1: dist[2]=min(4,1+2=3)=3, dist[3]=1+6=7.Step 3: Process node 2 and update distance
From 2: dist[3]=min(7,3+3=6)=6.Step 4: Final distances array
dist = [0, 1, 3, 6]Final Answer:
[0, 1, 3, 6] -> Option CQuick Check:
Distances updated correctly using topological order [OK]
- Not updating dist[2] to 3 via node 1
- Using wrong edge weights
- Printing dist before updates complete
Solution
Step 1: Initialize distances and update from node 0
dist = [0, inf, inf, inf, inf]. From 0: dist[1]=3, dist[2]=6.Step 2: Update distances from node 1 and 2
From 1: dist[2]=min(6,3+4=7)=6 (unchanged), dist[3]=3+4=7. From 2: dist[3]=min(7,6+8=14)=7 (unchanged).Step 3: Update distance from node 3 to 4
dist[4]=7+2=9.Step 4: Final shortest distance to node 4
The shortest distance to node 4 is 9.Final Answer:
9 -> Option BQuick Check:
Shortest path 0->1->3->4 = 9 [OK]
- Choosing longer path 0->2->3->4
- Adding weights incorrectly
- Ignoring topological order updates
Ready to prove it?
Pass the test and this pattern moves unknown
