Concept · Shortest path
A map has towns joined by roads, and each road has a length. You want the shortest route from one town to every other. Breadth-first search counts roads, not kilometres, so it picks the route with the fewest roads, which can be far longer.
Trying every route works but explodes: the number of routes grows exponentially with the number of roads. Repeating a search from each town with a "best so far" check is better but still wastes work, because the same town is reached again and again by routes we already know are worse.
We only need one number per town: the length of its shortest route. Which town's number can we be sure of right now?
Keep a tentative distance for every town: the shortest route found so far. It starts at ∞ for every town except the start, which is 0. Then repeat one move:
Take the unfinished town with the smallest tentative distance. Declare it final. Then look at its roads. For each neighbour, check whether going through this town is shorter than what the neighbour has. If it is, update the neighbour. This check is called a relaxation.
A min-heap makes "take the smallest" cheap. We push every improved distance into it as a (distance, town) pair.
Five towns, six roads, and the start is town 0. The number beside a town is its tentative distance. The number on a road is its length.
Start. Town 0 is final at 0. Its roads give town 1 a distance of 4 and town 2 a distance of 1.
An improvement. The smallest unfinished town is 2, at distance 1. Through it, town 1 costs 1 + 2 = 3, which beats the 4 it had. The distance of town 1 drops to 3, and a new entry goes into the heap.
A stale entry. The old entry (4, town 1) is still in the heap. When it comes out, town 1 already has 3, so the entry is stale and we throw it away. The same happens to (9, town 3) later, after town 3 improves to 8.
Done. Every town is popped once with its final distance: 0, 3, 1, 8 and 11. The roads that gave each town its distance form the shortest-path tree.
(0, start).(d, u). d is larger than dist[u]⟶stale, skip it(u, v, w): d + w < dist[v]⟶set dist[v] = d + w and push (d + w, v)Say we pop town u at distance d. Suppose a shorter route to u existed. It starts in the finished area and has to leave it through some road to an unfinished town x. That town is still waiting in the heap, so its distance is at least d, because we popped the smallest. The rest of the route from x to u can only add length, so the whole route is at least d. No shorter route exists, and d is final.
The step "the rest of the route can only add length" is where the proof needs non-negative roads. With a negative road, a long first leg followed by a big negative one could beat a town we already finalised. Dijkstra would then return a wrong answer without any error.
Pick an input, then press Play or use the arrow keys. The highlighted line is the one running, and the number beside each line counts how often it ran. The graph shows the tentative distance by each town and draws the current shortest-path tree. The heap on the right holds the pending entries, smallest on top.
dist holds V numbers. The heap holds at most 2E entries, because stale entries stay until they are popped.if d > dist[u]: continue, a town is expanded once for every entry it has. The answer is still correct, but the running time can grow badly.d + w for max(d, w) finds the route whose worst road is smallest, which is how grid problems like Swim in Rising Water use it.Reach for it when:
Problems that use it:
These are the questions that usually come next. Open one to see an answer you could say out loud.
What they are checkingWhether you know why the algorithm works, not only the steps. They want to see you point at the exact line of the proof that breaks.
A strong answerThe proof says a route through an unfinished town can only be longer. A negative road breaks that, so a town we finalised could later get a shorter route and Dijkstra would not notice. I would use Bellman-Ford, which is O(V × E), and it can also report a negative cycle. If the graph has no cycles, a topological order is faster.
What they are checkingWhether you tie the data structure to the shape of the input instead of using the same answer every time.
A strong answerWith E close to V², the heap costs about V² log V. Scanning an array for the smallest unfinished town costs O(V) per pick and O(V²) overall, with no heap overhead. So on a dense graph the plain array is better. On a sparse graph, where E is near V, the heap is better.
What they are checkingWhether you can stop work you do not need, and whether you can return the route itself instead of only its length.
A strong answerStop as soon as the target is popped, since its distance is final then. To return the route, record parent[v] = u each time a distance improves, and walk back from the target. It still explores everything closer than the target, so if the data allows a good estimate of the remaining distance, A* uses that to explore far fewer towns.