Back to Graph Theory Series

Part 9: Shortest Paths I — Dijkstra & Bellman-Ford

August 30, 2026 Wasil Zafar 19 min read

Every shortest-path algorithm in this series — BFS, Dijkstra, Bellman-Ford, and everything still to come — is the same relaxation idea wearing different clothes. This part is where we see the whole family tree at once.

Table of Contents

  1. Shortest-Path Fundamentals
  2. Relaxation as a Unifying Idea
  3. Choosing the Right Algorithm
  4. Difference Constraints
  5. A Glimpse of Specialized Techniques
  6. Exercises
  7. Conclusion & Next Steps

Shortest-Path Fundamentals

"Shortest path" is really five related questions, not one:

  • Single-source: distances from one vertex to every other vertex (BFS, Dijkstra, Bellman-Ford).
  • Single-destination: distances from every vertex to one target — solved by running a single-source algorithm on the reversed graph.
  • Single-pair: distance between one specific source and target — no asymptotically faster general algorithm exists than solving single-source and reading off one answer, though heuristics like A* (Part 10) exploit extra structure to prune the search.
  • All-pairs: distances between every pair of vertices (Floyd-Warshall, Johnson's — Part 10).
Mental Map

“Shortest path” changes mainly by what you ask for and what weights are allowed

Before choosing an algorithm, separate the query shape from the graph structure. Single-source asks for one origin to many destinations; all-pairs asks for every origin. Then ask whether the graph is unweighted, a DAG, non-negatively weighted, or allowed to contain negative edges.

Two questions come before the algorithm 1. Which distances do you need? 2. What kind of graph do you have? Single-source one source to all Single-pair one source to one target Single-destination all to one target All-pairs every pair Unweighted BFS structure DAG topological order Weights >= 0 Dijkstra-friendly Negative edges? Bellman-Ford territory

Memory aid: first identify the question you are asking; then identify what the edge weights permit.

Shortest-Path Trees

Every single-source algorithm we've built (BFS's parent map, Dijkstra's and Bellman-Ford's relaxation-driven parent updates) actually computes more than just distances — it computes a shortest-path tree: a spanning tree (of the reachable vertices) where the tree-path from the source to any vertex $v$ is a shortest path to $v$ in the original graph. This is why reconstructing an actual shortest path, not just its length, is always just a matter of walking parent pointers backward from the target to the source.

Visual Intuition

A shortest-path tree is the “receipt” for every distance

The distance label tells you how far a vertex is from the source; the parent pointer tells you why. Keeping one parent edge for each reachable vertex turns those choices into a tree. Follow parents backward from any target and you recover an actual shortest path.

Shortest-path tree from source S Teal edges are the chosen parent edges; gray edges still exist in the original graph. 2 5 3 1 2 10 8 S A B C D T d=0 d=2 d=5 d=5 d=6 d=8 Parent chain: T, D, B, S Reverse it to reconstruct: S, B, D, T

Tree Does Not Mean “Use Only Tree Edges in the Original Graph”

The original graph can have many extra edges. The shortest-path tree simply records one predecessor choice per reachable vertex that certifies a shortest route from the source.

Relaxation as a Unifying Idea

Every shortest-path algorithm in this series — without exception — is built from exactly one primitive operation, first named formally in Part 2:

$$\textbf{Relax}(u, v, w)\text{: if } d[u] + w(u,v) < d[v] \text{, set } d[v] \leftarrow d[u] + w(u,v) \text{ and } \pi[v] \leftarrow u$$
Relaxation

Relaxation asks one local question: “Did going through u improve our best route to v?”

A tentative distance is a best-known promise, not necessarily the final answer. Relaxing an edge compares the current promise for v against the route that first reaches u and then crosses that edge.

Before After 4 + 2 < 10; improvement exists d[v] becomes 6 and parent[v] becomes u w = 2 u v d[u] = 4 d[v] = 10 candidate via u = 4 + 2 = 6 w = 2 u v d[u] = 4 d[v] = 6 new certificate: parent[v] = u

Memory aid: relaxation is not “move along this edge.” It is “use this edge to challenge the best distance we currently know.”

What differs between BFS, Dijkstra, and Bellman-Ford is purely the order in which edges get relaxed:

AlgorithmRelaxation orderHandles negative weights?
BFS (deep dive)layer by layer (implicit — all weights = 1)n/a (unweighted)
Dijkstra (deep dive)always the currently-closest unfinalized vertexno
Bellman-Ford (deep dive)every edge, every round, $V-1$ timesyes
DAG shortest paths (Part 7)topological order, single passyes (DAG only)
Same Primitive, Different Schedule

The algorithms disagree about when an estimate is trustworthy

All four algorithms relax edges — the schedule changes BFS DAG DP Dijkstra Bellman-Ford Process by distance layers All edges behave like weight 1 Trust a whole layer Process topologically Dependencies can never point back Trust after predecessors Pick smallest tentative distance Needs non-negative edges Finalize one vertex Relax every edge repeatedly Allows negative edges Trust only after convergence

Key Insight

Dijkstra's algorithm is, in a precise sense, "Bellman-Ford with a smarter relaxation order" — a priority queue is really just a mechanism for guessing, correctly, which edge is safe to relax next without needing to revisit it later. That guess is only guaranteed safe when weights are non-negative, which is exactly why Dijkstra's algorithm and Bellman-Ford diverge exactly there.

Optimality Conditions

A clean way to verify a claimed set of distances $d[\cdot]$ without re-running any algorithm: $d[\cdot]$ is correct if and only if (1) $d[\text{source}] = 0$, (2) every edge $(u,v)$ satisfies $d[v] \leq d[u] + w(u,v)$ (no further relaxation is possible — the triangle inequality for shortest paths), and (3) every $d[v]$ with $v$ reachable equals the weight of some actual path. This is exactly the property Bellman-Ford's final "extra round" checks in reverse — if condition (2) is ever violated, the claimed distances are wrong.

Verification Intuition

A final distance labeling should survive every possible relaxation

Think of each edge as an attempted objection: “I can reach v more cheaply through u.” If even one edge succeeds, the distances are not finished. If every edge fails to improve anything, the labeling is locally stable; the path-witness condition ensures those labels are also achievable.

Stable edge Violation found u v w = 4 d[u] = 3 d[v] = 6 6 <= 3 + 4 = 7; no improvement u v w = 2 d[u] = 3 d[v] = 8 8 > 3 + 2 = 5; relax again

Choosing the Right Algorithm

The fastest correct algorithm is usually determined by one structural fact about the graph. Use the simplest branch whose assumptions your input actually satisfies.

Decision Guide

Pick the algorithm by eliminating unnecessary generality

What does the input allow? unweighted DAG weights >= 0 negative edges every pair BFS O(V + E) fewest edges = shortest Topo DP O(V + E) negative weights okay Dijkstra O((V+E) log V) greedy finalization Bellman-Ford O(VE) also detects neg. cycles Part 10 Floyd-Warshall or Johnson's The principle behind the tree Use the strongest structure you are given. More structure usually means less work. Do not pay Bellman-Ford's generality cost when BFS or DAG order is enough.

Difference Constraints

A genuinely surprising application: a system of inequalities of the form $x_j - x_i \leq c_{ij}$ (common in scheduling — "task $j$ must start at least $c_{ij}$ after task $i$") can be solved directly with Bellman-Ford. Build a graph with one vertex per variable, an edge $i \to j$ with weight $c_{ij}$ for every constraint, and a super-source connected to every vertex with weight-0 edges. Run Bellman-Ford from the super-source: the resulting distances $d[\cdot]$ are a valid assignment satisfying every constraint simultaneously, and a detected negative cycle means the constraint system is infeasible — no valid assignment exists at all.

Constraints → Graph

An inequality becomes an edge because relaxation enforces exactly that inequality

The constraint $x_j-x_i\le c_{ij}$ can be rewritten as $x_j\le x_i+c_{ij}$. That has the same shape as a shortest-path edge condition, so encode it as the directed edge $i\to j$ with weight $c_{ij}$.

$$x_j-x_i\le c_{ij}\quad\Longleftrightarrow\quad x_j\le x_i+c_{ij}$$
Difference constraints are shortest-path inequalities Every directed edge says: destination value is at most source value + edge weight. S x_C x_B x_A super-source -2 -3 0 0 0 One Bellman-Ford solution from the article: x_C = 0, x_B = -2, x_A = -5

Why the super-source? It gives Bellman-Ford a path to every variable, so every constraint participates in the same system.

Negative Cycle = Contradictory Constraints

If Bellman-Ford can keep decreasing values around a directed cycle, the inequalities collectively demand an impossible decrease. In this model, negative-cycle detection becomes a direct infeasibility certificate.

Worked Example

Turning "A must start ≥3 after B, B must start ≥2 after C" into a Graph

Constraints $x_A - x_B \leq -3$ and $x_B - x_C \leq -2$ become edges $B \to A$ (weight $-3$) and $C \to B$ (weight $-2$), plus a super-source $S$ with zero-weight edges to $A, B, C$. Running Bellman-Ford from $S$ gives one valid schedule (e.g., $x_C = 0, x_B = -2, x_A = -5$) — shift all values by a constant to make them non-negative if needed, since only the differences matter.

Difference ConstraintsScheduling

A Glimpse of Specialized Techniques

A few refinements worth knowing exist, each trading generality for speed in a specific setting: Dial's algorithm replaces Dijkstra's binary heap with an array of buckets when edge weights are small non-negative integers, achieving $O(V + E + W)$ where $W$ is the maximum weight. Bidirectional Dijkstra (the weighted cousin of bidirectional BFS from Part 5) searches from both the source and target simultaneously. Replacement paths ask "what's the shortest path if this one edge is removed?" for every edge on the current shortest path — useful for fault-tolerant routing. We'll meet A* — arguably the most important of these refinements — in full in Part 10.

Exercises

  1. Verify the optimality conditions by hand on the worked example from the Dijkstra deep dive — confirm every edge satisfies $d[v] \leq d[u] + w(u,v)$ for the final distances.
  2. Explain why single-destination shortest paths reduce to single-source shortest paths on the reversed graph, but this trick does not help for single-pair queries.
  3. Model the constraints $x - y \leq 2$, $y - z \leq 3$, $z - x \leq -6$ as a graph, run Bellman-Ford, and show the system is infeasible (hint: sum the three constraints — what do you get?).
  4. Challenge: Implement Dial's algorithm (bucket-based Dijkstra) for a graph with integer weights in $[0, 10]$, and compare its running time against a binary-heap Dijkstra on a large sparse graph.

Conclusion & Next Steps

Every shortest-path algorithm is relaxation with a different edge order, verifiable by the same three optimality conditions, and — via difference constraints — solves scheduling problems that don't look like graphs at all until you draw them as one. Single-source is done; next, we tackle the case where you need distances between every pair of vertices at once.

Next in the Series

In Part 10: Shortest Paths II — Floyd-Warshall, Johnson's & A*, we solve all-pairs shortest paths two different ways and meet the heuristic-guided search that powers GPS routing and video-game pathfinding.