Unit 1 · Algorithms · Lesson 16 of 24
Shortest paths and their assumptions
Step 1: Learn the ideas
- Relaxation and the shortest-path tree
- BFS for unit weights
- 0-1 BFS with a deque
- Dijkstra: settled-set invariant, binary heap, lazy deletion
- Why negative edges break Dijkstra
- Bellman-Ford, negative cycles and hop-limited relaxation
- Shortest paths in DAGs by topological order
- State-expanded graphs (vertex plus extra state)
- Bottleneck (minimax) paths
- Floyd-Warshall and Johnson for all pairs (awareness)
Why it matters
Weighted-graph questions reward knowing assumptions. The natural follow-ups are why your algorithm is correct, what happens with negative weights, and what changes when the number of hops is limited. The same ideas run real systems: link-state routing protocols compute Dijkstra-style shortest-path trees, distance-vector routing is a distributed form of Bellman-Ford, and navigation and scheduling systems use variants with heuristics or precomputation. Knowing which assumption each algorithm needs is what separates a memorized template from an engineering answer.
Core ideas
Relaxation. All of these algorithms keep an upper bound d[v] on each distance and improve it with relax(u, v): if d[u] + w(u, v) < d[v], set d[v] and record parent[v] = u. They differ only in the order of relaxations and in the assumptions that make that order sufficient. Parent pointers form a shortest-path tree for reconstructing routes.
Unit weights: BFS. Queue order already equals distance order, in O(V + E).
Weights 0 or 1: 0-1 BFS. Use a deque: after relaxing along a 0-weight edge push the vertex to the front, along a 1-weight edge to the back. The deque stays sorted and holds at most two distance values, so it acts like Dijkstra with a bucket queue, in O(V + E). A vertex can be pushed twice; skip stale entries when popped. Grids where some moves are free and others cost one (such as removing an obstacle) fit this.
Non-negative weights: Dijkstra. Repeatedly take the unsettled vertex u with the smallest tentative distance, settle it and relax its edges. Invariant: a settled vertex's distance is final. Argument: any other path to u leaves the settled set at some first vertex y; d[y] is at most the length of that path's prefix, d[u] ≤ d[y] because u was the minimum, and the rest of the path cannot be negative. Standard-library heaps rarely support decrease-key, so push a new (distance, vertex) pair after every improvement and skip popped entries whose distance exceeds the recorded best (lazy deletion). The heap holds O(E) entries, giving O(E log E) = O(E log V), usually written O((V + E) log V).
import heapq
def dijkstra(adj, source):
dist = {source: 0}
heap = [(0, source)]
while heap:
d, u = heapq.heappop(heap)
if d > dist[u]:
continue # stale entry: lazy deletion
for v, w in adj[u]:
nd = d + w
if nd < dist.get(v, float('inf')):
dist[v] = nd
heapq.heappush(heap, (nd, v))
return dist
Why negative edges break it. With edges s -> a (2), s -> b (3) and b -> a (-2), Dijkstra settles a at 2 before it looks at b, although s -> b -> a costs 1, and everything relaxed from a used the wrong value. Code that re-pushes improved vertices, like the sketch above, may repair small cases, but it loses Dijkstra's guarantee and can take exponential time. Adding a constant to every edge does not help: it penalizes paths with more edges and changes which path is shortest. Johnson's algorithm instead reweights with vertex potentials computed by Bellman-Ford, which preserves shortest paths.
Negative weights: Bellman-Ford. Without a reachable negative cycle, some shortest path to every vertex uses at most V - 1 edges, so V - 1 rounds of relaxing every edge suffice: after round i, d[v] is at most the best length over paths with at most i edges. O(VE). If a further round still improves a distance, a negative cycle is reachable and distances to vertices it reaches are undefined. For "at most k edges", run k rounds but relax from a copy of the previous round's distances; in-place updates can chain several edges in one round and silently exceed the limit.
Other cases. In a DAG, relaxing vertices in topological order takes O(V + E), even with negative weights. Floyd-Warshall computes all pairs in O(V^3) and suits small dense graphs. When a path's cost is its largest edge (minimum effort, rising water), Dijkstra still works because extending a path never lowers its cost; binary search on the answer with BFS, or union-find over sorted edges, are alternatives. When a constraint matters (stops used, keys held), search over expanded states (vertex, state).
Common misconceptions
- "Dijkstra works with negative edges as long as there is no negative cycle." The settle-once argument fails; use Bellman-Ford, or Johnson-style reweighting for all pairs.
- "Mark vertices visited when pushing them, as in BFS." A vertex's distance can still improve after its first push; it is final only when popped with its current best distance.
- "Lazy deletion makes the heap grow without bound." There is at most one entry per successful relaxation, so O(E) entries, and log E is at most 2 log V.
- "In-place Bellman-Ford respects a hop limit." Copy the previous round's distances whenever the number of edges matters.
- "Bellman-Ford always runs V - 1 full rounds." It can stop as soon as a round changes nothing; V - 1 is the worst case.
How to approach it
Start every weighted problem by stating the weight model and the path-cost function (sum, maximum, product), then pick the algorithm. Say the invariant before writing code ("a popped entry that is not stale is final") and write the stale-entry check first, since it is the line most often forgotten. Test with a graph where a path with more edges is cheaper, an unreachable vertex and zero-weight edges. Graduate-level interviews usually expect a correct Dijkstra with its complexity; mid and senior candidates should expect questions on negative weights, hop limits, path reconstruction, or recomputing routes after an edge changes.
Suggested order
- 1Read MIT 6.006 Lecture 11 (Weighted Shortest Paths) for relaxation, shortest-path trees and DAG relaxation.
- 2Read MIT 6.006 Lecture 13 (Dijkstra's Algorithm), focusing on the correctness argument and where non-negative weights are used.
- 3Implement Dijkstra with lazy deletion from memory, then hand-run the textbook settle-once version on the three-vertex negative-edge example and find where it goes wrong.
- 4Solve Network Delay Time with Dijkstra, then Shortest Path in Binary Matrix with plain BFS and explain why no heap is needed.
- 5Read MIT 6.006 Lecture 12 (Bellman-Ford) and Erickson Ch. 8 (Shortest Paths); then solve Cheapest Flights Within K Stops with round-copied Bellman-Ford, and again with Dijkstra over (city, stops) states.
- 6Solve Path With Minimum Effort (bottleneck cost) and Minimum Obstacle Removal to Reach Corner (0-1 BFS); keep Swim in Rising Water for review and solve it two different ways.
- 7Skim Erickson Ch. 9 (All-Pairs Shortest Paths) or MIT 6.006 Lecture 14 only far enough to say when Floyd-Warshall or Johnson reweighting is appropriate.
Step 2: Check off what you can do
Tap each one you can genuinely do or explain. Each earns 10 XP.
Step 3: Go deeper
MIT 6.006 Introduction to Algorithms (Spring 2020)Start here
MIT OpenCourseWareSection: Lecture 13: Dijkstra's Algorithm
Dijkstra correctness proof and running time; Lectures 11 and 12 on the same index cover relaxation, DAG relaxation and Bellman-Ford.
Open(opens in a new tab)Link responded, 5 Oct 2026Algorithms (Jeff Erickson) with companion lecture notesStart here
Jeff Erickson, University of Illinois Urbana-ChampaignSection: Ch. 8: Shortest Paths
Unified view of shortest paths as generic relaxation (Ford's framework), with BFS, DAG, Dijkstra and Bellman-Ford as special cases.
Open(opens in a new tab)Link responded, 5 Oct 2026Competitive Programmer’s Handbook
CSESSection: Ch. 13: Shortest paths
Compact implementations of Bellman-Ford, Dijkstra with a priority queue and Floyd-Warshall, including the skip-processed-node check.
Open(opens in a new tab)Link responded, 5 Oct 2026Algorithms, 4th Edition booksite
Princeton UniversitySection: 4.4 Shortest Paths
Edge-weighted digraph code for Dijkstra, acyclic shortest paths and Bellman-Ford with negative-cycle detection.
Open(opens in a new tab)Link responded, 5 Oct 2026
Step 4: Practice
Before each problem, write the weight model and the path-cost function in one line and name the algorithm they imply. Solve Cheapest Flights Within K Stops twice (Bellman-Ford with copied rounds, then Dijkstra over expanded states) and compare. Use Swim in Rising Water for review rotation: it can be solved with Dijkstra, with binary search plus BFS, or with union-find over sorted cells.
- Network Delay TimeLeetCode(opens in a new tab)Not attemptedLeetCode
- Shortest Path in Binary MatrixLeetCode(opens in a new tab)Not attemptedLeetCode
- Cheapest Flights Within K StopsLeetCode(opens in a new tab)Not attemptedLeetCode
- Path With Minimum EffortLeetCode(opens in a new tab)Not attemptedLeetCode
- Minimum Obstacle Removal to Reach CornerLeetCode(opens in a new tab)Not attemptedLeetCode
- Swim in Rising WaterLeetCode(opens in a new tab)Not attemptedLeetCode
Record attempts on each problem’s page (+15 XP, +10 more when solved on your own).
Step 5: Test yourself
Answer each one out loud or on paper before you finish the lesson.
- Question 1What invariant does Dijkstra maintain about settled vertices, and which assumption does it need?
- Question 2Why does lazy deletion keep the running time at O((V + E) log V)?
- Question 3When does 0-1 BFS apply, and why is a deque enough?
- Question 4How does Bellman-Ford detect a negative cycle, and what does it mean for vertices reachable from it?
- Question 5Why does adding a constant to every edge weight not make Dijkstra safe for negative edges?
- Question 6How do you reconstruct the actual path rather than only its cost?
You’re done when you can
- Implement Dijkstra with lazy deletion from memory in under 12 minutes, handling stale heap entries correctly.
- Give a three-vertex counterexample showing why Dijkstra fails with a negative edge, without notes.
- Solve one unseen weighted-grid or flight-style problem in under 30 minutes, choosing the algorithm from the constraints.
- Explain why hop-limited Bellman-Ford must relax from a copy of the previous round.


