208. Network Delay Time
Problem
Given n>=1 network nodes labeled 1 through n, directed edges (u,v,delay) with nonnegative delays, and a starting node, return the earliest time by which every node receives a signal. Return -1 if any node is unreachable. Signal propagation may branch simultaneously.
Examples
Example 1
network_delay(4,[(2,1,1),(2,3,1),(3,4,1)],2)
=> 2The last arrival is at node 4 after two time units.
Example 2
network_delay(2,[(1,2,1)],2)
=> -1Directed edges do not let node 2 send backward to node 1.
Example 3
network_delay(1,[],1)
=> 0The source already has the signal.
Reveal the worked solution and reasoning
Why this mechanism works
Each node receives the signal at its shortest travel time from the source. Parallel propagation means total completion time is the maximum of those shortest distances, not their sum. Translate one-based labels at the boundary and reuse Dijkstra's nonnegative-weight protocol.
State and boundaries
The zero-based adjacency models the same directed network. distances[v] is the earliest arrival time at node v+1.
Protocol
- Convert each label to zero-based adjacency.
- Run Dijkstra from start-1.
- Return -1 if any distance is None.
- Otherwise return the maximum distance.
Visualization
source 2 -> 1 in 1; source 2 -> 3 in 1; 3 -> 4 in 1
arrival times [1,0,1,2]
all reached at time max=2Python implementation
from kata_207 import dijkstra
def network_delay(n, edges, start):
adj = [[] for _ in range(n)]
for u, v, delay in edges:
adj[u - 1].append((v - 1, delay))
distances = dijkstra(adj, start - 1)
return -1 if any(value is None for value in distances) else max(distances)
Worked execution
network_delay(4,[(2,1,1),(2,3,1),(3,4,1)],2)
L4 network_delay: adj = [[] for _ in range(n)] n=4; edges=[(2, 1, 1), (2, 3, 1), (3, 4, 1)]; start=2; _=0 L4 network_delay: adj = [[] for _ in range(n)] n=4; edges=[(2, 1, 1), (2, 3, 1), (3, 4, 1)]; start=2; _=1 L4 network_delay: adj = [[] for _ in range(n)] n=4; edges=[(2, 1, 1), (2, 3, 1), (3, 4, 1)]; start=2; _=2 L4 network_delay: adj = [[] for _ in range(n)] n=4; edges=[(2, 1, 1), (2, 3, 1), (3, 4, 1)]; start=2; _=3 L4 network_delay: adj = [[] for _ in range(n)] n=4; edges=[(2, 1, 1), (2, 3, 1), (3, 4, 1)]; start=2; adj=[[], [], [], []] L5 network_delay: for u, v, delay in edges: n=4; edges=[(2, 1, 1), (2, 3, 1), (3, 4, 1)]; start=2; adj=[[], [], [], []]; u=2; v=1; delay=1 L6 network_delay: adj[u - 1].append((v - 1, delay)) n=4; edges=[(2, 1, 1), (2, 3, 1), (3, 4, 1)]; start=2; adj=[[], [(..., ...)], [], []]; u=2; v=1; delay=1 L5 network_delay: for u, v, delay in edges: n=4; edges=[(2, 1, 1), (2, 3, 1), (3, 4, 1)]; start=2; adj=[[], [(..., ...)], [], []]; u=2; v=3; delay=1 ... 5 intermediate state changes omitted ... L8 <genexpr>: return -1 if any(value is None for value in distances) else max(distances) .0=list_iterator; value=0 L8 <genexpr>: return -1 if any(value is None for value in distances) else max(distances) .0=list_iterator; value=1 L8 <genexpr>: return -1 if any(value is None for value in distances) else max(distances) .0=list_iterator; value=2 L8 <genexpr>: return -1 if any(value is None for value in distances) else max(distances) .0=list_iterator; value=2
Result: 2. The last arrival is at node 4 after two time units.
network_delay(2,[(1,2,1)],2)
L4 network_delay: adj = [[] for _ in range(n)] n=2; edges=[(1, 2, 1)]; start=2; _=0 L4 network_delay: adj = [[] for _ in range(n)] n=2; edges=[(1, 2, 1)]; start=2; _=1 L4 network_delay: adj = [[] for _ in range(n)] n=2; edges=[(1, 2, 1)]; start=2; adj=[[], []] L5 network_delay: for u, v, delay in edges: n=2; edges=[(1, 2, 1)]; start=2; adj=[[], []]; u=1; v=2; delay=1 L6 network_delay: adj[u - 1].append((v - 1, delay)) n=2; edges=[(1, 2, 1)]; start=2; adj=[[(..., ...)], []]; u=1; v=2; delay=1 L7 network_delay: distances = dijkstra(adj, start - 1) n=2; edges=[(1, 2, 1)]; start=2; adj=[[(..., ...)], []]; u=1; v=2; delay=1; distances=[None, 0] L8 <genexpr>: return -1 if any(value is None for value in distances) else max(distances) .0=list_iterator; value=None
Result: -1. Directed edges do not let node 2 send backward to node 1.
Why the solution is correct
Dijkstra gives the earliest achievable arrival at every reachable node. All nodes have received the signal exactly when the last of these arrivals occurs, so their maximum is necessary and sufficient. An unreachable node can never receive the signal.
Time and space
O(n+E log(E+2)) time and O(n+E) space, inherited from adjacency construction and lazy-heap Dijkstra.
Common mistakes
Summing arrival times incorrectly serializes concurrent propagation. Forgetting to convert both endpoints and the start mixes label conventions.
Reasoning card
State: The zero-based adjacency models the same directed network. distances[v] is the earliest arrival time at node v+1.
Invariant: Each finalized distance is the earliest arrival time.
Decision to rehearse: Run Dijkstra and take the maximum finite shortest distance.
Finish: Otherwise return the maximum distance
Transfer exercise
Allow several initial sources by initializing each at distance zero in the same heap.