Network Delay Time is a clean, classic application of Dijkstra's algorithm — the standard tool for shortest paths on a graph with non-negative edge weights. If you can trace Dijkstra, you can solve a large family of "fastest / cheapest path" interview problems.
Problem. You are given a network of n nodes and a list of travel times times, where each entry
(u, v, w) means a signal takes w time to go from node u to node v (directed). A signal starts at
source node k. Return the time it takes for all n nodes to receive the signal, or -1 if some
node can never be reached.
Example: n = 5, k = 1, edges (1,2,2) (1,3,4) (2,3,1) (2,4,7) (3,4,2) (4,5,3) → answer 8 (the
slowest node to hear the signal is node 5, at distance 8).
The slow way first
You could try every possible path from the source to every node and keep the shortest — but the number of paths explodes, so that is hopelessly slow. Even repeatedly scanning all nodes to find the next closest one (plain Dijkstra without a heap) costs O(n²), which is fine for tiny graphs but wasteful.
The real question: given the distances I know so far, which node is definitely finished? The answer is always the closest unfinalized node — nothing shorter can reach it later. A min-heap hands us that node instantly.
The idea: always expand the closest node
Keep a dist map (best-known time to each node) and a min-heap of (distance, node) pairs. Repeatedly pop the smallest. That node is now finalized — its distance can never improve. Relax each outgoing edge: if going through this node gives a shorter distance to a neighbor, update it and push the new pair. When the heap is empty, every reachable node holds its true shortest distance.
The answer is the maximum finalized distance: the signal has reached everyone only once the slowest node has heard it. If any node is still missing from dist at the end, it was unreachable, so we return -1.
Walk through it
Step through the animation. The source node 1 lights up first. Each pop finalizes the closest node (turning it green) and relaxes its edges, lowering distances in the panel on the right. Watch dist[3] drop from 4 to 3 and dist[4] drop from 9 to 5 as shorter routes appear. A stale heap entry — (4, 3) after node 3 was already finalized at 3 — gets popped and skipped. The last node to finalize is node 5 at distance 8, which is the answer.
Pseudocode
build adjacency list: node -> list of (neighbor, weight)
dist = { source: 0 }
heap = [ (0, source) ]
while heap is not empty:
(d, u) = pop smallest from heap
if d > dist[u]: # a stale, outdated entry
skip it
for each (v, w) in neighbors of u:
nd = d + w
if nd < dist[v]: # found a shorter route to v
dist[v] = nd
push (nd, v) onto heap
if some node never got a distance:
return -1
return the largest value in distThe Python solution
def network_delay_time(times, n, k):
graph = build_adjacency(times)
dist = {k: 0}
heap = [(0, k)]
while heap:
d, u = heappop(heap)
if d > dist.get(u, inf):
continue
for v, w in graph[u]:
nd = d + w
if nd < dist.get(v, inf):
dist[v] = nd
heappush(heap, (nd, v))
if len(dist) < n:
return -1
return max(dist.values())graphmaps each node to its list of(neighbor, weight)edges.distholds the best-known distance; the source starts at 0 and is the only known node.- The heap is ordered by distance, so
heappopalways returns the closest unfinalized node. - Line 6 pops that node. The
d > dist.get(u, inf)check skips stale entries — an old, larger distance we already improved on. - Lines 11-13 are the relaxation: if
d + wbeats the neighbor's current distance, record it and push the new pair. - At the end, an unreachable node is simply absent from
dist, solen(dist) < nmeans return-1; otherwise the answer is the largest distance.
Complexity
| Case | Time | Notes |
|---|---|---|
| Plain Dijkstra (scan for min) | O(n²) (slow) | rescan all nodes each step |
| Heap Dijkstra (this solution) | O(E log V) (moderate) | each edge pushes once |
O(V + E) (moderate)With a min-heap, each edge causes at most one push, and every heap operation is O(log V), giving O(E log V) — the standard bound for Dijkstra on a sparse graph.
When this pattern shows up
Whenever a problem asks for the shortest / fastest / cheapest path on a graph with non-negative weights, reach for Dijkstra with a min-heap. Network Delay Time, Path with Minimum Effort, Cheapest Flights, and Swim in Rising Water are all the same move: a priority queue that always expands the best-known frontier node.
Dijkstra assumes non-negative edge weights. With negative edges, a node finalized early could later be improved, breaking the algorithm — use Bellman-Ford instead. Also remember the lazy-deletion trick: skip a popped entry whose distance is worse than the one already recorded.
Practice
After node 2 is finalized at distance 2, we relax the edge 2 to 3 with weight 1. dist[3] was 4. What does it become, and why?
1. Why can we treat a node as finalized the moment it is popped from the heap?
2. What does relaxing an edge (u to v, weight w) do?
3. How is the final answer computed?
4. Why does the code skip a popped entry when d > dist[u]?