Dijkstra's Algorithm
What is this?
BFS finds the shortest path when every step costs the same, but real roads aren't like that β some stretches are long, some are quick. Dijkstra's algorithm is the upgrade for maps where each connection has a length or cost. It works like a patient explorer who always visits the nearest unexplored place next: settle the closest spot first, note any shortcuts it reveals, and repeat. Because you never look at a faraway place before a nearer one, the first time you lock in a spot, you've already found its cheapest route.
π‘ Fun fact: Edsger Dijkstra invented this algorithm in 1956 β and in about twenty minutes, in his head, while sitting at a cafΓ© in Amsterdam with his fiancΓ©e, no pen or paper. He later said the lack of writing materials forced him to keep it simple, which is exactly why it turned out so elegant. Your GPS still runs a descendant of those twenty minutes.
π The 2 problems in this chapter are free. Sign in with Google or Microsoft to start solving.
The one-line idea: to find the shortest path from one source to every other node in a graph with non-negative weights, repeatedly settle the closest unsettled node and relax its edges. Because no edge can ever make a path cheaper, the first time you pop a node off a min-heap you already hold its final shortest distance β a greedy choice that's provably optimal. It's BFS upgraded for weighted graphs.
1. What "single-source shortest path" really means
You have a weighted graph and a start node s. You want dist[v] β the cheapest total weight to reach every v from s. The naive idea (try all paths) is exponential. Dijkstra makes it tractable with one invariant:
Process nodes in increasing order of their distance from the source. Maintain a tentative dist[], all β except dist[s] = 0. Pull the smallest tentative distance off a min-heap, mark it settled, and relax its outgoing edges:
The key guarantee: once a node is popped, its distance is final. With non-negative weights, every other route to it must pass through nodes already at least that far away, so it can't be improved.
β οΈ This is exactly where negative weights break Dijkstra. A later, cheaper edge could undercut an already-settled node β but it's been frozen. For negative edges you need Bellman-Ford (O(VΒ·E), handles negatives, detects negative cycles).
2. Where you'll actually meet this
Shortest-path-on-weighted-graphs is one of the most deployed algorithms in production:
- Network routing. OSPF (Open Shortest Path First), the backbone interior-gateway protocol, runs Dijkstra over a link-state map where edge weights are link costs. Each router computes shortest paths to every subnet.
- Maps & GPS. Driving/transit directions are shortest paths where weights are travel time or distance. Production systems layer A* (Dijkstra + a goal heuristic) and contraction hierarchies on top, but the core is Dijkstra.
- Latency-aware / cost-aware routing. Service meshes and CDNs route requests along least-latency or least-cost paths; weights are measured RTTs or egress prices.
- Games & robotics. Pathfinding on weighted terrain (mud costs more than road); A* is the genre standard, Dijkstra its uniform-cost special case.
- Finance & ops research. Cheapest currency-conversion or transport routes β modeled as min-cost paths over weighted graphs.
If a problem says shortest / fastest / cheapest path, minimum time to reach, non-negative weights β this is your tool.
3. The Dijkstra toolkit
- Min-heap as the engine. A priority queue keyed on tentative distance gives you the closest unsettled node in O(log V). This is what makes it O((V + E) log V) instead of O(VΒ²).
- Lazy deletion. Heaps can't cheaply update a key, so just push duplicates on every improvement and skip stale pops (
if d > dist[u]: continue). Simpler and fast in practice. - Relaxation only forward. When you settle
u, try to improve each neighbourv. Never relax into already-settled nodes β their distances are final. - Settle-on-pop, not on-push. A node's distance is committed the moment it leaves the heap, not when first discovered. This ordering is the correctness proof.
- Reconstruct paths via parents. Store
prev[v]on each successful relaxation; walk back from target to source to recover the actual route, not just its length.
The canonical heap implementation:
import heapq
def dijkstra(graph, source, n):
# graph[u] = list of (neighbor, weight), weights >= 0
dist = [float('inf')] * n
dist[source] = 0
pq = [(0, source)] # (tentative_distance, node)
while pq:
d, u = heapq.heappop(pq) # closest UNSETTLED node
if d > dist[u]:
continue # stale duplicate β already settled cheaper
for v, w in graph[u]: # relax every outgoing edge
nd = d + w
if nd < dist[v]:
dist[v] = nd
heapq.heappush(pq, (nd, v)) # lazy: push, don't decrease-key
return dist # dist[v] = shortest distance source -> v
4. A 30-second worked example
Source A, find shortest distances:
Notice C was tentatively 7 via B, then improved to 5 via DβE. That late improvement is fine because C hadn't been popped yet β relaxation kept refining it until it was settled.
5. Problems / when to reach for it
- Network Delay Time β time for a signal from one node to reach all nodes = the maximum of Dijkstra's shortest distances (and
βif any node is unreachable). - Path with Maximum Probability β multiply probabilities along edges; take
-log(p)as the weight so "most probable" becomes "shortest," then run Dijkstra (all weights non-negative since0 β€ p β€ 1). - Cheapest Flights within K Stops β Dijkstra variant with a hop budget (often better solved with Bellman-Ford-style relaxation rounds).
- Reach for Dijkstra whenever weights are non-negative and you need shortest paths from one source. Target: O((V + E) log V) time, O(V) space.
6. Common pitfalls π«
- Negative weights. Dijkstra fails silently β it returns wrong answers, not an error. Use Bellman-Ford (or SPFA) when any edge is negative.
- Forgetting lazy-deletion / settled check. Without
if d > dist[u]: continue, you reprocess stale heap entries, inflating runtime (and sometimes redundantly relaxing). - Tuple order in the heap. Distance must be the first element so the heap orders by distance, not node id.
- Using BFS on a weighted graph. Plain BFS counts edges, not weight β correct only when every edge weight is equal. Otherwise you need Dijkstra (or 0-1 BFS for weights in {0,1}).
- Not handling unreachable nodes. Disconnected vertices stay at
β; decide whether that's a valid answer or a-1/error per the problem.
7. Key takeaways
- Greedy + min-heap, settle the closest first. The first pop of a node is its final shortest distance β that's the whole algorithm and its proof.
- Relaxation is the operation:
dist[v] = min(dist[v], dist[u] + w), applied forward from each settled node. - Non-negative weights are mandatory. Negative edges silently break the greedy invariant β switch to Bellman-Ford.
- It's weighted BFS. Equal weights β BFS suffices; differing weights β Dijkstra; known goal + heuristic β A*.
- O((V + E) log V) with a binary heap, the version you should reach for in interviews β and the version OSPF and your GPS quietly run millions of times a day.
Minimum Score of a Path Between Two Cities
Core idea: The "score" of a path is its weakest (minimum-weight) edge, and you're allowed to revisit roads and cities as many times as you like. That single freedom collapses the whole problem: if you can wander freely, you can splice any edge in city 1's connected component into your path. So the minimum score is simply the smallest edge weight anywhere in the component reachable from city 1 β found with one BFS/DFS in O(V + E). No shortest path, no Dijkstra, no weights to add up.
1. The problem, in a fresh setting
You manage a regional water-pipeline network. There are n pumping stations labelled 1β¦n, joined by bidirectional pipes. Each pipe [a, b, w] connects stations a and b and can carry water at a maximum rate w (litres/sec). When water travels along a route of pipes, the throughput of that route is set by its narrowest pipe β the weakest link bottlenecks everything, exactly like a chain.
You need to send water from station 1 to station n. You may route the water however you want β looping back, reusing pipes, taking detours β there's no cost for distance. The question your operations team keeps asking: across every possible route from 1 to n, what's the smallest "narrowest pipe" you'd be forced to accept? (LeetCode calls this the minimum score of a path.)
Formally: given an undirected weighted graph with n nodes and edge list roads (each [a, b, dist]), the score of a path is the minimum edge weight on it. Return the minimum score of any path from node 1 to node n. You may reuse nodes and edges, and nodes 1 and n are guaranteed to be in the same component.
| n | roads | Answer | Why |
|---|---|---|---|
| 4 | [[1,2,9],[2,3,6],[2,4,5],[1,4,7]] |
5 |
All four cities form one component; the smallest edge anywhere is 5. |
| 4 | [[1,2,2],[1,3,4],[3,4,7]] |
2 |
Component {1,2,3,4}; smallest edge is the 1β2 pipe of weight 2. |
| 2 | [[1,2,3]] |
3 |
Only one pipe exists, so it's both the path and the answer. |
Note: LeetCode uses 1-indexed cities (
1β¦n). The arithmetic is the same in any language; just be consistent.
Sign in to continue reading
The rest of this lesson is available with a free account. Signing in with Google or Microsoft is free.
Sign in to read the full lesson