interviewpath
  • 0day streak
  • 10total XP
  • Level 1
0/6

Unit 1 · Algorithms · Lesson 16 of 24

Shortest paths and their assumptions

Shortest-path algorithms differ mainly in what they assume about edge weights: unit weights need only BFS, 0/1 weights a deque, non-negative weights Dijkstra, and negative weights Bellman-Ford. Interviewers probe whether you know these assumptions and why breaking them breaks the algorithm; routing and scheduling systems rest on the same reasoning.
About 4 h 30 minFocus mode

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

  1. 1Read MIT 6.006 Lecture 11 (Weighted Shortest Paths) for relaxation, shortest-path trees and DAG relaxation.
  2. 2Read MIT 6.006 Lecture 13 (Dijkstra's Algorithm), focusing on the correctness argument and where non-negative weights are used.
  3. 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.
  4. 4Solve Network Delay Time with Dijkstra, then Shortest Path in Binary Matrix with plain BFS and explain why no heap is needed.
  5. 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.
  6. 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.
  7. 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 OpenCourseWare

    Section: 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 2026
  • Algorithms (Jeff Erickson) with companion lecture notesStart here

    Jeff Erickson, University of Illinois Urbana-Champaign

    Section: 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 2026
  • Competitive Programmer’s Handbook

    CSES

    Section: 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 2026
  • Algorithms, 4th Edition booksite

    Princeton University

    Section: 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.

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.

  1. Question 1What invariant does Dijkstra maintain about settled vertices, and which assumption does it need?
  2. Question 2Why does lazy deletion keep the running time at O((V + E) log V)?
  3. Question 3When does 0-1 BFS apply, and why is a deque enough?
  4. Question 4How does Bellman-Ford detect a negative cycle, and what does it mean for vertices reachable from it?
  5. Question 5Why does adding a constant to every edge weight not make Dijkstra safe for negative edges?
  6. 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.

Finish the lesson

How confident do you feel?

Lesson complete!

+50 XP

Reviews will come up on the Practice tab so it sticks.

Your notes and study time

Log study time