3.5.10 · D2Graphs

Visual walkthrough — Dijkstra's algorithm — greedy, priority queue, O((V+E) log V) — no negative edges

2,114 words10 min readBack to topic

We use the parent's tiny graph the whole way through so nothing sneaks up on you:

A→B(4), A→C(1), C→B(2), C→D(5), B→D(1), source A.


Step 1 — What is a graph, really?

WHAT. A graph is just dots (we call them vertices or nodes — think "cities") joined by arrows (we call them edges — think "one-way roads"). Each arrow carries a number, its weight, meaning "how many minutes this road costs to walk."

WHY. Before we talk about "shortest," we must agree on what a "path" and its "length" even are. A path from A to D is any sequence of arrows you can follow, front-to-back, from A to D. Its length is the sum of the weights of the arrows you used — not the number of arrows, the sum of their minute-costs.

PICTURE. Look at the board. Four cities A B C D. The arrow A→C is labelled , so walking directly from A to C costs minute. The arrow A→B costs . There are two ways to reach B from A: the direct road A→B (cost ), or the detour A→C→B (cost ). The detour is cheaper even though it uses more roads — hold onto that, it's the whole game.

Figure — Dijkstra's algorithm — greedy, priority queue, O((V+E) log V) — no negative edges

Step 2 — The sticky-note array, and as "no idea yet"

WHAT. We keep one number per city, written — the best time we currently believe. At the start we know nothing, so every city gets ("infinity" = "unreachable as far as I know"), except the source: , because standing at home costs zero minutes.

WHY and not, say, ? Because is a value that any real path beats. The first time we find any route to a city, its finite cost is automatically smaller than , so the sticky note updates. Using a big-but-finite number risks a real path being larger than it and getting rejected.

PICTURE. Four sticky notes on the board: A = 0 (chalk-yellow, we're sure), B = ∞, C = ∞, D = ∞ (all pale, "unknown"). Nothing is finalized yet — a note can still shrink.

Figure — Dijkstra's algorithm — greedy, priority queue, O((V+E) log V) — no negative edges

Step 3 — Relaxation: the one move we ever make

WHAT. The only action Dijkstra performs is relaxing an edge :

WHY "relax"? The word comes from a tight rubber band: the distance estimate for is stretched too high, and passing through lets it relax down to a truer, smaller value. It can only ever go down, never up.

PICTURE. We stand at (with ) and relax its two out-roads. Road A→C(1): , so 's note drops to . Road A→B(4): , so 's note drops to . The blue arrows show the estimate falling from onto a real number.

Figure — Dijkstra's algorithm — greedy, priority queue, O((V+E) log V) — no negative edges

Step 4 — The priority queue: always grab the nearest unfinished city

WHAT. After Step 3 two cities have finite guesses: C = 1 and B = 4. Which do we trust and "finalize" first? We use a priority queue (a min-heap): a gadget that always hands back the smallest stored (distance, city) pair fast. We pop the minimum: C(1).

WHY the smallest? This is the heart of the whole method. Claim: the city with the smallest current estimate can never be improved later, so it is safe to declare done. Steps 5–6 prove exactly this. For now, feel it: C = 1 is the closest thing to home; no other route can sneak in under because every road costs , so any other first step already costs at least as much.

PICTURE. The heap drawn as a little sorted rack: C(1) at the front (chalk-pink highlight = "about to pop"), B(4) behind it. The arrow pulls C out and stamps it FINAL.

Figure — Dijkstra's algorithm — greedy, priority queue, O((V+E) log V) — no negative edges

Step 5 — Why the popped city is truly done (the proof, drawn)

WHAT. We prove the invariant: the moment we pop the min-distance city , its note already equals the true shortest distance .

WHY (by contradiction — drawn on the board). Suppose not: some secret cheaper path to exists with true cost . That secret path starts inside the finalized region (it starts at , which is done) and must eventually step out of it. Let be the first not-yet-finalized city on that secret path, entered from a finalized city via the road .

PICTURE. The board is split: a shaded "DONE" blob (finalized cities) and the outside. The secret pink path crosses the border exactly at the road .

Figure — Dijkstra's algorithm — greedy, priority queue, O((V+E) log V) — no negative edges

Now chase the inequalities, each pinned to the picture:

  • First ::: since is finalized, we already relaxed , so 's note is at most that sum.
  • The ::: on the shortest path, is precisely plus the one road to .
  • Second ::: the only place non-negativity is used — the remaining road-costs from to are all , so getting further can't get cheaper.
  • Final ::: our false assumption.

So . But we popped because it was the minimum, meaning . Contradiction. Therefore no secret cheaper path exists — is genuinely done. ∎


Step 6 — Finalizing C fixes B (greed paying off)

WHAT. C is done at . We relax C's out-roads. C→B(2): , so B's note drops from 4 to 3 — the detour beats the direct road, exactly as we teased in Step 1. C→D(5): , so D = 6.

WHY this is the payoff. Had we finalized B at first (its old estimate), we'd have locked in a wrong answer. Popping the closest city first — C before B — is what let C's cheaper route reach B before B was frozen. Greed and correctness are the same act here.

PICTURE. C (now FINAL) fires two blue relaxation arrows: one crosses out B's 4 and writes 3; one turns D's into 6.

Figure — Dijkstra's algorithm — greedy, priority queue, O((V+E) log V) — no negative edges

Step 7 — Finish the flood, and the stale-entry twist

WHAT. The heap now holds B(3), B(4) (a leftover!), D(6). Pop the min B(3), finalize it, relax B→D(1): , so D = 4. Next pop is the stale B(4): but B is already done and , so the guard if d > dist[u]: continue skips it. Finally pop D(4), no out-roads, done.

WHY the stale skip exists. We used a lazy heap: rather than editing B's old entry when it improved, we just pushed a fresh B(3) and left the old B(4) behind. So a city can appear more than once. The first pop carries its true value; any later pop carries an outdated, larger d — we recognize and discard it.

PICTURE. The heap rack shows B(3) popped (final), then B(4) popped but stamped STALE / SKIP in pink because , then D(4) popped final.

Figure — Dijkstra's algorithm — greedy, priority queue, O((V+E) log V) — no negative edges

Final answer, matching the parent's table exactly:


Step 8 — Degenerate cases you must never be surprised by

WHAT / WHY / PICTURE, three corner scenarios on one board:

  1. Unreachable city. If no path exists to some city, its note stays forever — the flood never arrives. (That's correct output, not a bug.)
  2. Zero-weight edge . The relaxation and proof still hold: "" includes . Two cities can share the same distance; the heap just pops them in some tie-order.
  3. Self-loop or already-shorter route. Relaxing u→v when changes nothing — the < test simply fails, note untouched.
Figure — Dijkstra's algorithm — greedy, priority queue, O((V+E) log V) — no negative edges
Recall Why we never revisit a finalized city

Because Step 5 proved the popped city is truly optimal. Once optimal, no future relaxation can lower it (all roads ), so re-examining it is wasted work. Contrast: with even one negative edge that guarantee vanishes — see Bellman-Ford algorithm.


The one-picture summary

Everything above, compressed: cities flood outward from A in the order water reaches them — A(0) → C(1) → B(3) → D(4) — each finalized only when popped as the current minimum, each relaxing its out-roads to shrink neighbors' sticky notes, with the "" road-costs guaranteeing no finalized note ever moves again.

Figure — Dijkstra's algorithm — greedy, priority queue, O((V+E) log V) — no negative edges
Recall Feynman: the whole walkthrough in plain words

Home is A. Every house has a sticky note saying "fastest time I know," starting at 0 for home and "unknown (∞)" for everyone else. I repeat one move: look at all houses I haven't finalized, walk to the nearest one, stamp it done — because roads only ever cost time (never give it back), nothing can later beat that nearest house's time. Then from that just-finalized house I check each outgoing road: if going through here is faster than a neighbor's current note, I lower the neighbor's note. A min-heap is just a fast way to always grab the nearest un-done house. Sometimes a house's note improves before I visit it, so it might sit in the heap twice; when I pop an old, too-big copy I recognize it's stale and toss it. When the heap empties, every sticky note holds the true shortest time. For our graph the water reaches A→C→B→D at times 0, 1, 3, 4.


Connections

  • Yeh visual walkthrough Hinglish mein
  • Priority Queue (Binary Heap) — the min-heap doing the "nearest first" pops
  • Greedy algorithms — Step 5 is the exchange/invariant argument
  • BFS — Step 4's ordering becomes a plain queue when all weights are
  • Bellman-Ford algorithm — what to reach for when the "" step dies
  • A* search — Dijkstra with a heuristic nudging the flood toward the goal
  • Prim's algorithm — same pop-min-relax skeleton, different relaxation
  • Johnson's algorithm — reweight to non-negative, then run this walkthrough