Bellman-Ford
Bellman-Ford relaxes every edge in repeated passes: after k passes all shortest paths of at most k edges are correct, so n - 1 passes suffice, and one more pass reveals a reachable negative cycle.
Learning goals
- Run Bellman-Ford and say why n - 1 passes are enough
- Stop early when a pass changes nothing
- Detect a negative cycle and extract it
- Separate vertices with distance minus infinity from the rest
Prerequisites
Dijkstra fails with negative edges, and negative edges are real: currency exchange, energy gains, refunds. Bellman-Ford handles them, at the price of O(n · m) time, and it tells you when the question has no answer.
Negative edges
Bellman-Ford does not rely on any order of vertices. It relaxes **every** edge, in any order, and repeats this pass. The negative weights do no harm because no distance is ever declared final until the end. In the first preset a negative edge lowers a distance that an earlier pass had already set.
The result is null (or a flag) when a negative cycle is reachable from the source. The first branch of the animation shows the passes edge by edge, with the current distances beside each vertex.
1static long[] bellmanFord(int n, int[][] edges, int s) { // edges {u, v, w}; null on a reachable negative cycle2long INF = Long.MAX_VALUE / 2;3long[] dist = new long[n];4Arrays.fill(dist, INF);5dist[s] = 0;6for (int round = 1; round < n; round++) { // a shortest path has at most n - 1 edges7boolean changed = false;8for (int[] e : edges)9if (dist[e[0]] != INF && dist[e[0]] + e[2] < dist[e[1]]) { dist[e[1]] = dist[e[0]] + e[2]; changed = true; }10if (!changed) break; // nothing improved: finished early11}12for (int[] e : edges) // one more pass: any improvement means a negative cycle13if (dist[e[0]] != INF && dist[e[0]] + e[2] < dist[e[1]]) return null;14return dist;15}
Bellman-Ford and SPFA: shortest paths with negative edges and cycle detection
Interactive · edit the inputdirected graph · 5 vertices · 9 edges
d=0
dist[v]
dist[0] = 0 and every other distance is unknown (∞). Relaxing an edge u → v with weight w means: if dist[u] + w is smaller than dist[v], use it. A shortest path has at most n - 1 = 4 edges, so n - 1 passes over all edges are enough.
1long[] bellmanFord(int n, int[][] edges, int s) { // edges: {u, v, w}; null on a reachable negative cycle2long INF = Long.MAX_VALUE / 2;3long[] dist = new long[n]; Arrays.fill(dist, INF); dist[s] = 0;4for (int round = 1; round < n; round++) { // a shortest path has at most n - 1 edges5boolean changed = false;6for (int[] e : edges)7if (dist[e[0]] != INF && dist[e[0]] + e[2] < dist[e[1]]) { dist[e[1]] = dist[e[0]] + e[2]; changed = true; }8if (!changed) break; // nothing improved: finished early9}10for (int[] e : edges) // one more pass: any improvement means a negative cycle11if (dist[e[0]] != INF && dist[e[0]] + e[2] < dist[e[1]]) return null;12return dist;13}14long[] spfa(int n, List<int[]>[] adj, int s) { // adj[u] = {v, w}; null on a reachable negative cycle15long INF = Long.MAX_VALUE / 2;16long[] dist = new long[n]; Arrays.fill(dist, INF); dist[s] = 0;17int[] len = new int[n]; boolean[] inQ = new boolean[n];18Deque<Integer> q = new ArrayDeque<>(); q.add(s); inQ[s] = true;19while (!q.isEmpty()) {20int u = q.poll(); inQ[u] = false;21for (int[] e : adj[u]) {22if (dist[u] + e[1] >= dist[e[0]]) continue;23dist[e[0]] = dist[u] + e[1]; len[e[0]] = len[u] + 1;24if (len[e[0]] >= n) return null; // a best path with n edges repeats a vertex25if (!inQ[e[0]]) { q.add(e[0]); inQ[e[0]] = true; }26}27}28return dist;29}
Live state
source
0
Execution trace (dry run table)Show 32 stepsHide
| Step | Line | State | What happens |
|---|---|---|---|
| 1 | L3 | source=0 | dist[0] = 0 and every other distance is unknown (∞). Relaxing an edge u → v with weight w means: if dist[u] + w is smaller than dist[v], use it. A shortest path has at most n - 1 = 4 edges, so n - 1 passes over all edges are enough. |
| 2 | L7 | round=1 u=0 v=1 dist[v]=6 | Round 1, 0 → 1: 0 + 6 = 6 is better, so dist[1] = 6. |
| 3 | L7 | round=1 u=0 v=2 dist[v]=7 | Round 1, 0 → 2: 0 + 7 = 7 is better, so dist[2] = 7. |
| 4 | L7 | round=1 edge=1 → 2 candidate=14 | Round 1, 1 → 2: 6 + 8 = 14 is not better than 7: no change. |
| 5 | L7 | round=1 u=1 v=3 dist[v]=11 | Round 1, 1 → 3: 6 + 5 = 11 is better, so dist[3] = 11. |
| 6 | L7 | round=1 u=1 v=4 dist[v]=2 | Round 1, 1 → 4: 6 + -4 = 2 is better, so dist[4] = 2. |
| 7 | L7 | round=1 u=2 v=3 dist[v]=4 | Round 1, 2 → 3: 7 + -3 = 4 is better, so dist[3] = 4. |
| 8 | L7 | round=1 edge=2 → 4 candidate=16 | Round 1, 2 → 4: 7 + 9 = 16 is not better than 2: no change. |
| 9 | L7 | round=1 u=3 v=1 dist[v]=2 | Round 1, 3 → 1: 4 + -2 = 2 is better, so dist[1] = 2. |
| 10 | L7 | round=1 edge=4 → 3 candidate=9 | Round 1, 4 → 3: 2 + 7 = 9 is not better than 4: no change. |
| 11 | L9 | round=1 dist=[0, 2, 7, 4, 2] | End of round 1: distances [0, 2, 7, 4, 2]. Paths with up to 1 edges are now correct. |
| 12 | L7 | round=2 edge=0 → 1 candidate=6 | Round 2, 0 → 1: 0 + 6 = 6 is not better than 2: no change. |
| 13 | L7 | round=2 edge=0 → 2 candidate=7 | Round 2, 0 → 2: 0 + 7 = 7 is not better than 7: no change. |
| 14 | L7 | round=2 edge=1 → 2 candidate=10 | Round 2, 1 → 2: 2 + 8 = 10 is not better than 7: no change. |
| 15 | L7 | round=2 edge=1 → 3 candidate=7 | Round 2, 1 → 3: 2 + 5 = 7 is not better than 4: no change. |
| 16 | L7 | round=2 u=1 v=4 dist[v]=-2 | Round 2, 1 → 4: 2 + -4 = -2 is better, so dist[4] = -2. |
| 17 | L7 | round=2 edge=2 → 3 candidate=4 | Round 2, 2 → 3: 7 + -3 = 4 is not better than 4: no change. |
| 18 | L7 | round=2 edge=2 → 4 candidate=16 | Round 2, 2 → 4: 7 + 9 = 16 is not better than -2: no change. |
| 19 | L7 | round=2 edge=3 → 1 candidate=2 | Round 2, 3 → 1: 4 + -2 = 2 is not better than 2: no change. |
| 20 | L7 | round=2 edge=4 → 3 candidate=5 | Round 2, 4 → 3: -2 + 7 = 5 is not better than 4: no change. |
| 21 | L9 | round=2 dist=[0, 2, 7, 4, -2] | End of round 2: distances [0, 2, 7, 4, -2]. Paths with up to 2 edges are now correct. |
| 22 | L7 | round=3 edge=0 → 1 candidate=6 | Round 3, 0 → 1: 0 + 6 = 6 is not better than 2: no change. |
| 23 | L7 | round=3 edge=0 → 2 candidate=7 | Round 3, 0 → 2: 0 + 7 = 7 is not better than 7: no change. |
| 24 | L7 | round=3 edge=1 → 2 candidate=10 | Round 3, 1 → 2: 2 + 8 = 10 is not better than 7: no change. |
| 25 | L7 | round=3 edge=1 → 3 candidate=7 | Round 3, 1 → 3: 2 + 5 = 7 is not better than 4: no change. |
| 26 | L7 | round=3 edge=1 → 4 candidate=-2 | Round 3, 1 → 4: 2 + -4 = -2 is not better than -2: no change. |
| 27 | L7 | round=3 edge=2 → 3 candidate=4 | Round 3, 2 → 3: 7 + -3 = 4 is not better than 4: no change. |
| 28 | L7 | round=3 edge=2 → 4 candidate=16 | Round 3, 2 → 4: 7 + 9 = 16 is not better than -2: no change. |
| 29 | L7 | round=3 edge=3 → 1 candidate=2 | Round 3, 3 → 1: 4 + -2 = 2 is not better than 2: no change. |
| 30 | L7 | round=3 edge=4 → 3 candidate=5 | Round 3, 4 → 3: -2 + 7 = 5 is not better than 4: no change. |
| 31 | L9 | round=3 | Round 3 changed nothing, so no later round can: stop early. |
| 32 | L12 | return=[0, 2, 7, 4, -2] relaxations=7 | No edge improves in the check pass: no negative cycle is reachable. Distances: [0, 2, 7, 4, -2]. Cost O(n · m). |
Repeated relaxation
**Why n - 1 passes?** After pass k, every vertex whose shortest path has at most k edges has its correct distance (by induction: the last edge of such a path is relaxed in pass k, after its tail was correct from pass k - 1). A shortest path with no repeated vertex has at most n - 1 edges, so n - 1 passes finish the job when there is no negative cycle.
A pass may fix more than one edge of a path if the edges are processed in a lucky order, so fewer passes can be enough. The function below counts the passes that changed something; if a pass changes nothing, no later pass can, and you can stop. In the worst case the order of edges is unlucky and all n - 1 passes are needed.
1static int passesUntilStable(int n, int[][] edges, int s) { // passes that changed something; -1 on a reachable negative cycle2long INF = Long.MAX_VALUE / 2;3long[] dist = new long[n];4Arrays.fill(dist, INF);5dist[s] = 0;6int passes = 0;7for (int round = 1; round <= n; round++) {8boolean changed = false;9for (int[] e : edges)10if (dist[e[0]] != INF && dist[e[0]] + e[2] < dist[e[1]]) { dist[e[1]] = dist[e[0]] + e[2]; changed = true; }11if (!changed) return passes;12if (round == n) return -1; // pass n still improves: a negative cycle13passes++;14}15return passes;16}
Bellman-Ford and SPFA: shortest paths with negative edges and cycle detection
Interactive · edit the inputdirected graph · 5 vertices · 9 edges
d=0
dist[v]
dist[0] = 0 and every other distance is unknown (∞). Relaxing an edge u → v with weight w means: if dist[u] + w is smaller than dist[v], use it. A shortest path has at most n - 1 = 4 edges, so n - 1 passes over all edges are enough.
1long[] bellmanFord(int n, int[][] edges, int s) { // edges: {u, v, w}; null on a reachable negative cycle2long INF = Long.MAX_VALUE / 2;3long[] dist = new long[n]; Arrays.fill(dist, INF); dist[s] = 0;4for (int round = 1; round < n; round++) { // a shortest path has at most n - 1 edges5boolean changed = false;6for (int[] e : edges)7if (dist[e[0]] != INF && dist[e[0]] + e[2] < dist[e[1]]) { dist[e[1]] = dist[e[0]] + e[2]; changed = true; }8if (!changed) break; // nothing improved: finished early9}10for (int[] e : edges) // one more pass: any improvement means a negative cycle11if (dist[e[0]] != INF && dist[e[0]] + e[2] < dist[e[1]]) return null;12return dist;13}14long[] spfa(int n, List<int[]>[] adj, int s) { // adj[u] = {v, w}; null on a reachable negative cycle15long INF = Long.MAX_VALUE / 2;16long[] dist = new long[n]; Arrays.fill(dist, INF); dist[s] = 0;17int[] len = new int[n]; boolean[] inQ = new boolean[n];18Deque<Integer> q = new ArrayDeque<>(); q.add(s); inQ[s] = true;19while (!q.isEmpty()) {20int u = q.poll(); inQ[u] = false;21for (int[] e : adj[u]) {22if (dist[u] + e[1] >= dist[e[0]]) continue;23dist[e[0]] = dist[u] + e[1]; len[e[0]] = len[u] + 1;24if (len[e[0]] >= n) return null; // a best path with n edges repeats a vertex25if (!inQ[e[0]]) { q.add(e[0]); inQ[e[0]] = true; }26}27}28return dist;29}
Live state
source
0
Execution trace (dry run table)Show 32 stepsHide
| Step | Line | State | What happens |
|---|---|---|---|
| 1 | L3 | source=0 | dist[0] = 0 and every other distance is unknown (∞). Relaxing an edge u → v with weight w means: if dist[u] + w is smaller than dist[v], use it. A shortest path has at most n - 1 = 4 edges, so n - 1 passes over all edges are enough. |
| 2 | L7 | round=1 u=0 v=1 dist[v]=6 | Round 1, 0 → 1: 0 + 6 = 6 is better, so dist[1] = 6. |
| 3 | L7 | round=1 u=0 v=2 dist[v]=7 | Round 1, 0 → 2: 0 + 7 = 7 is better, so dist[2] = 7. |
| 4 | L7 | round=1 edge=1 → 2 candidate=14 | Round 1, 1 → 2: 6 + 8 = 14 is not better than 7: no change. |
| 5 | L7 | round=1 u=1 v=3 dist[v]=11 | Round 1, 1 → 3: 6 + 5 = 11 is better, so dist[3] = 11. |
| 6 | L7 | round=1 u=1 v=4 dist[v]=2 | Round 1, 1 → 4: 6 + -4 = 2 is better, so dist[4] = 2. |
| 7 | L7 | round=1 u=2 v=3 dist[v]=4 | Round 1, 2 → 3: 7 + -3 = 4 is better, so dist[3] = 4. |
| 8 | L7 | round=1 edge=2 → 4 candidate=16 | Round 1, 2 → 4: 7 + 9 = 16 is not better than 2: no change. |
| 9 | L7 | round=1 u=3 v=1 dist[v]=2 | Round 1, 3 → 1: 4 + -2 = 2 is better, so dist[1] = 2. |
| 10 | L7 | round=1 edge=4 → 3 candidate=9 | Round 1, 4 → 3: 2 + 7 = 9 is not better than 4: no change. |
| 11 | L9 | round=1 dist=[0, 2, 7, 4, 2] | End of round 1: distances [0, 2, 7, 4, 2]. Paths with up to 1 edges are now correct. |
| 12 | L7 | round=2 edge=0 → 1 candidate=6 | Round 2, 0 → 1: 0 + 6 = 6 is not better than 2: no change. |
| 13 | L7 | round=2 edge=0 → 2 candidate=7 | Round 2, 0 → 2: 0 + 7 = 7 is not better than 7: no change. |
| 14 | L7 | round=2 edge=1 → 2 candidate=10 | Round 2, 1 → 2: 2 + 8 = 10 is not better than 7: no change. |
| 15 | L7 | round=2 edge=1 → 3 candidate=7 | Round 2, 1 → 3: 2 + 5 = 7 is not better than 4: no change. |
| 16 | L7 | round=2 u=1 v=4 dist[v]=-2 | Round 2, 1 → 4: 2 + -4 = -2 is better, so dist[4] = -2. |
| 17 | L7 | round=2 edge=2 → 3 candidate=4 | Round 2, 2 → 3: 7 + -3 = 4 is not better than 4: no change. |
| 18 | L7 | round=2 edge=2 → 4 candidate=16 | Round 2, 2 → 4: 7 + 9 = 16 is not better than -2: no change. |
| 19 | L7 | round=2 edge=3 → 1 candidate=2 | Round 2, 3 → 1: 4 + -2 = 2 is not better than 2: no change. |
| 20 | L7 | round=2 edge=4 → 3 candidate=5 | Round 2, 4 → 3: -2 + 7 = 5 is not better than 4: no change. |
| 21 | L9 | round=2 dist=[0, 2, 7, 4, -2] | End of round 2: distances [0, 2, 7, 4, -2]. Paths with up to 2 edges are now correct. |
| 22 | L7 | round=3 edge=0 → 1 candidate=6 | Round 3, 0 → 1: 0 + 6 = 6 is not better than 2: no change. |
| 23 | L7 | round=3 edge=0 → 2 candidate=7 | Round 3, 0 → 2: 0 + 7 = 7 is not better than 7: no change. |
| 24 | L7 | round=3 edge=1 → 2 candidate=10 | Round 3, 1 → 2: 2 + 8 = 10 is not better than 7: no change. |
| 25 | L7 | round=3 edge=1 → 3 candidate=7 | Round 3, 1 → 3: 2 + 5 = 7 is not better than 4: no change. |
| 26 | L7 | round=3 edge=1 → 4 candidate=-2 | Round 3, 1 → 4: 2 + -4 = -2 is not better than -2: no change. |
| 27 | L7 | round=3 edge=2 → 3 candidate=4 | Round 3, 2 → 3: 7 + -3 = 4 is not better than 4: no change. |
| 28 | L7 | round=3 edge=2 → 4 candidate=16 | Round 3, 2 → 4: 7 + 9 = 16 is not better than -2: no change. |
| 29 | L7 | round=3 edge=3 → 1 candidate=2 | Round 3, 3 → 1: 4 + -2 = 2 is not better than 2: no change. |
| 30 | L7 | round=3 edge=4 → 3 candidate=5 | Round 3, 4 → 3: -2 + 7 = 5 is not better than 4: no change. |
| 31 | L9 | round=3 | Round 3 changed nothing, so no later round can: stop early. |
| 32 | L12 | return=[0, 2, 7, 4, -2] relaxations=7 | No edge improves in the check pass: no negative cycle is reachable. Distances: [0, 2, 7, 4, -2]. Cost O(n · m). |
Negative-cycle detection
If a pass number n still improves some distance, the graph has a negative cycle reachable from the source, because otherwise n - 1 passes would already have settled everything. To *find* the cycle, remember for each vertex the edge that last improved it; starting from a vertex improved in pass n and stepping back n times along those edges lands inside the cycle, and one more lap collects it.
Starting every distance at 0 (as if a virtual source reached every vertex for free) finds a negative cycle anywhere, not only ones reachable from a chosen source. The edges are returned in order around the cycle. The *Negative cycle reachable from 0* preset ends with the check pass that fails.
1static List<Integer> negativeCycleEdges(int n, int[][] edges) { // indexes of the edges of one negative cycle, in order; empty if none2long[] dist = new long[n]; // all 0: a virtual source reaches every vertex3int[] viaEdge = new int[n];4Arrays.fill(viaEdge, -1);5int last = -1;6for (int round = 0; round < n; round++) {7last = -1;8for (int k = 0; k < edges.length; k++) {9int[] e = edges[k];10if (dist[e[0]] + e[2] < dist[e[1]]) { dist[e[1]] = dist[e[0]] + e[2]; viaEdge[e[1]] = k; last = e[1]; }11}12if (last == -1) return new ArrayList<>(); // a pass changed nothing: no negative cycle13}14int v = last;15for (int i = 0; i < n; i++) v = edges[viaEdge[v]][0]; // n steps back along the improving edges lands inside the cycle16List<Integer> cycle = new ArrayList<>();17int u = v;18do {19int k = viaEdge[u];20cycle.add(k);21u = edges[k][0];22} while (u != v);23Collections.reverse(cycle); // now in forward order24return cycle;25}
Bellman-Ford and SPFA: shortest paths with negative edges and cycle detection
Interactive · edit the inputdirected graph · 5 vertices · 9 edges
d=0
dist[v]
dist[0] = 0 and every other distance is unknown (∞). Relaxing an edge u → v with weight w means: if dist[u] + w is smaller than dist[v], use it. A shortest path has at most n - 1 = 4 edges, so n - 1 passes over all edges are enough.
1long[] bellmanFord(int n, int[][] edges, int s) { // edges: {u, v, w}; null on a reachable negative cycle2long INF = Long.MAX_VALUE / 2;3long[] dist = new long[n]; Arrays.fill(dist, INF); dist[s] = 0;4for (int round = 1; round < n; round++) { // a shortest path has at most n - 1 edges5boolean changed = false;6for (int[] e : edges)7if (dist[e[0]] != INF && dist[e[0]] + e[2] < dist[e[1]]) { dist[e[1]] = dist[e[0]] + e[2]; changed = true; }8if (!changed) break; // nothing improved: finished early9}10for (int[] e : edges) // one more pass: any improvement means a negative cycle11if (dist[e[0]] != INF && dist[e[0]] + e[2] < dist[e[1]]) return null;12return dist;13}14long[] spfa(int n, List<int[]>[] adj, int s) { // adj[u] = {v, w}; null on a reachable negative cycle15long INF = Long.MAX_VALUE / 2;16long[] dist = new long[n]; Arrays.fill(dist, INF); dist[s] = 0;17int[] len = new int[n]; boolean[] inQ = new boolean[n];18Deque<Integer> q = new ArrayDeque<>(); q.add(s); inQ[s] = true;19while (!q.isEmpty()) {20int u = q.poll(); inQ[u] = false;21for (int[] e : adj[u]) {22if (dist[u] + e[1] >= dist[e[0]]) continue;23dist[e[0]] = dist[u] + e[1]; len[e[0]] = len[u] + 1;24if (len[e[0]] >= n) return null; // a best path with n edges repeats a vertex25if (!inQ[e[0]]) { q.add(e[0]); inQ[e[0]] = true; }26}27}28return dist;29}
Live state
source
0
Execution trace (dry run table)Show 32 stepsHide
| Step | Line | State | What happens |
|---|---|---|---|
| 1 | L3 | source=0 | dist[0] = 0 and every other distance is unknown (∞). Relaxing an edge u → v with weight w means: if dist[u] + w is smaller than dist[v], use it. A shortest path has at most n - 1 = 4 edges, so n - 1 passes over all edges are enough. |
| 2 | L7 | round=1 u=0 v=1 dist[v]=6 | Round 1, 0 → 1: 0 + 6 = 6 is better, so dist[1] = 6. |
| 3 | L7 | round=1 u=0 v=2 dist[v]=7 | Round 1, 0 → 2: 0 + 7 = 7 is better, so dist[2] = 7. |
| 4 | L7 | round=1 edge=1 → 2 candidate=14 | Round 1, 1 → 2: 6 + 8 = 14 is not better than 7: no change. |
| 5 | L7 | round=1 u=1 v=3 dist[v]=11 | Round 1, 1 → 3: 6 + 5 = 11 is better, so dist[3] = 11. |
| 6 | L7 | round=1 u=1 v=4 dist[v]=2 | Round 1, 1 → 4: 6 + -4 = 2 is better, so dist[4] = 2. |
| 7 | L7 | round=1 u=2 v=3 dist[v]=4 | Round 1, 2 → 3: 7 + -3 = 4 is better, so dist[3] = 4. |
| 8 | L7 | round=1 edge=2 → 4 candidate=16 | Round 1, 2 → 4: 7 + 9 = 16 is not better than 2: no change. |
| 9 | L7 | round=1 u=3 v=1 dist[v]=2 | Round 1, 3 → 1: 4 + -2 = 2 is better, so dist[1] = 2. |
| 10 | L7 | round=1 edge=4 → 3 candidate=9 | Round 1, 4 → 3: 2 + 7 = 9 is not better than 4: no change. |
| 11 | L9 | round=1 dist=[0, 2, 7, 4, 2] | End of round 1: distances [0, 2, 7, 4, 2]. Paths with up to 1 edges are now correct. |
| 12 | L7 | round=2 edge=0 → 1 candidate=6 | Round 2, 0 → 1: 0 + 6 = 6 is not better than 2: no change. |
| 13 | L7 | round=2 edge=0 → 2 candidate=7 | Round 2, 0 → 2: 0 + 7 = 7 is not better than 7: no change. |
| 14 | L7 | round=2 edge=1 → 2 candidate=10 | Round 2, 1 → 2: 2 + 8 = 10 is not better than 7: no change. |
| 15 | L7 | round=2 edge=1 → 3 candidate=7 | Round 2, 1 → 3: 2 + 5 = 7 is not better than 4: no change. |
| 16 | L7 | round=2 u=1 v=4 dist[v]=-2 | Round 2, 1 → 4: 2 + -4 = -2 is better, so dist[4] = -2. |
| 17 | L7 | round=2 edge=2 → 3 candidate=4 | Round 2, 2 → 3: 7 + -3 = 4 is not better than 4: no change. |
| 18 | L7 | round=2 edge=2 → 4 candidate=16 | Round 2, 2 → 4: 7 + 9 = 16 is not better than -2: no change. |
| 19 | L7 | round=2 edge=3 → 1 candidate=2 | Round 2, 3 → 1: 4 + -2 = 2 is not better than 2: no change. |
| 20 | L7 | round=2 edge=4 → 3 candidate=5 | Round 2, 4 → 3: -2 + 7 = 5 is not better than 4: no change. |
| 21 | L9 | round=2 dist=[0, 2, 7, 4, -2] | End of round 2: distances [0, 2, 7, 4, -2]. Paths with up to 2 edges are now correct. |
| 22 | L7 | round=3 edge=0 → 1 candidate=6 | Round 3, 0 → 1: 0 + 6 = 6 is not better than 2: no change. |
| 23 | L7 | round=3 edge=0 → 2 candidate=7 | Round 3, 0 → 2: 0 + 7 = 7 is not better than 7: no change. |
| 24 | L7 | round=3 edge=1 → 2 candidate=10 | Round 3, 1 → 2: 2 + 8 = 10 is not better than 7: no change. |
| 25 | L7 | round=3 edge=1 → 3 candidate=7 | Round 3, 1 → 3: 2 + 5 = 7 is not better than 4: no change. |
| 26 | L7 | round=3 edge=1 → 4 candidate=-2 | Round 3, 1 → 4: 2 + -4 = -2 is not better than -2: no change. |
| 27 | L7 | round=3 edge=2 → 3 candidate=4 | Round 3, 2 → 3: 7 + -3 = 4 is not better than 4: no change. |
| 28 | L7 | round=3 edge=2 → 4 candidate=16 | Round 3, 2 → 4: 7 + 9 = 16 is not better than -2: no change. |
| 29 | L7 | round=3 edge=3 → 1 candidate=2 | Round 3, 3 → 1: 4 + -2 = 2 is not better than 2: no change. |
| 30 | L7 | round=3 edge=4 → 3 candidate=5 | Round 3, 4 → 3: -2 + 7 = 5 is not better than 4: no change. |
| 31 | L9 | round=3 | Round 3 changed nothing, so no later round can: stop early. |
| 32 | L12 | return=[0, 2, 7, 4, -2] relaxations=7 | No edge improves in the check pass: no negative cycle is reachable. Distances: [0, 2, 7, 4, -2]. Cost O(n · m). |
Reachable negative cycles
A negative cycle only matters for vertices that can be reached *through* it. A negative cycle that the source cannot reach changes nothing (the third preset shows distances are still fine). And if only some vertices lie behind the cycle, others keep a finite distance.
To tell them apart, run n - 1 passes, mark the head of every edge that can still be improved, then spread the mark along all edges: every marked vertex has distance minus infinity. This is how contest problems ask for -INF per vertex.
1static boolean[] unboundedBelow(int n, int[][] edges, int s) { // true where the distance from s is -infinity2long INF = Long.MAX_VALUE / 2;3long[] dist = new long[n];4Arrays.fill(dist, INF);5dist[s] = 0;6for (int round = 1; round < n; round++)7for (int[] e : edges)8if (dist[e[0]] != INF && dist[e[0]] + e[2] < dist[e[1]]) dist[e[1]] = dist[e[0]] + e[2];9boolean[] bad = new boolean[n];10Deque<Integer> stack = new ArrayDeque<>();11for (int[] e : edges) // an edge that still improves is on or behind a negative cycle12if (dist[e[0]] != INF && dist[e[0]] + e[2] < dist[e[1]] && !bad[e[1]]) { bad[e[1]] = true; stack.push(e[1]); }13while (!stack.isEmpty()) {14int u = stack.pop();15for (int[] e : edges)16if (e[0] == u && !bad[e[1]]) { bad[e[1]] = true; stack.push(e[1]); }17}18return bad;19}
Bellman-Ford and SPFA: shortest paths with negative edges and cycle detection
Interactive · edit the inputdirected graph · 5 vertices · 9 edges
d=0
dist[v]
dist[0] = 0 and every other distance is unknown (∞). Relaxing an edge u → v with weight w means: if dist[u] + w is smaller than dist[v], use it. A shortest path has at most n - 1 = 4 edges, so n - 1 passes over all edges are enough.
1long[] bellmanFord(int n, int[][] edges, int s) { // edges: {u, v, w}; null on a reachable negative cycle2long INF = Long.MAX_VALUE / 2;3long[] dist = new long[n]; Arrays.fill(dist, INF); dist[s] = 0;4for (int round = 1; round < n; round++) { // a shortest path has at most n - 1 edges5boolean changed = false;6for (int[] e : edges)7if (dist[e[0]] != INF && dist[e[0]] + e[2] < dist[e[1]]) { dist[e[1]] = dist[e[0]] + e[2]; changed = true; }8if (!changed) break; // nothing improved: finished early9}10for (int[] e : edges) // one more pass: any improvement means a negative cycle11if (dist[e[0]] != INF && dist[e[0]] + e[2] < dist[e[1]]) return null;12return dist;13}14long[] spfa(int n, List<int[]>[] adj, int s) { // adj[u] = {v, w}; null on a reachable negative cycle15long INF = Long.MAX_VALUE / 2;16long[] dist = new long[n]; Arrays.fill(dist, INF); dist[s] = 0;17int[] len = new int[n]; boolean[] inQ = new boolean[n];18Deque<Integer> q = new ArrayDeque<>(); q.add(s); inQ[s] = true;19while (!q.isEmpty()) {20int u = q.poll(); inQ[u] = false;21for (int[] e : adj[u]) {22if (dist[u] + e[1] >= dist[e[0]]) continue;23dist[e[0]] = dist[u] + e[1]; len[e[0]] = len[u] + 1;24if (len[e[0]] >= n) return null; // a best path with n edges repeats a vertex25if (!inQ[e[0]]) { q.add(e[0]); inQ[e[0]] = true; }26}27}28return dist;29}
Live state
source
0
Execution trace (dry run table)Show 32 stepsHide
| Step | Line | State | What happens |
|---|---|---|---|
| 1 | L3 | source=0 | dist[0] = 0 and every other distance is unknown (∞). Relaxing an edge u → v with weight w means: if dist[u] + w is smaller than dist[v], use it. A shortest path has at most n - 1 = 4 edges, so n - 1 passes over all edges are enough. |
| 2 | L7 | round=1 u=0 v=1 dist[v]=6 | Round 1, 0 → 1: 0 + 6 = 6 is better, so dist[1] = 6. |
| 3 | L7 | round=1 u=0 v=2 dist[v]=7 | Round 1, 0 → 2: 0 + 7 = 7 is better, so dist[2] = 7. |
| 4 | L7 | round=1 edge=1 → 2 candidate=14 | Round 1, 1 → 2: 6 + 8 = 14 is not better than 7: no change. |
| 5 | L7 | round=1 u=1 v=3 dist[v]=11 | Round 1, 1 → 3: 6 + 5 = 11 is better, so dist[3] = 11. |
| 6 | L7 | round=1 u=1 v=4 dist[v]=2 | Round 1, 1 → 4: 6 + -4 = 2 is better, so dist[4] = 2. |
| 7 | L7 | round=1 u=2 v=3 dist[v]=4 | Round 1, 2 → 3: 7 + -3 = 4 is better, so dist[3] = 4. |
| 8 | L7 | round=1 edge=2 → 4 candidate=16 | Round 1, 2 → 4: 7 + 9 = 16 is not better than 2: no change. |
| 9 | L7 | round=1 u=3 v=1 dist[v]=2 | Round 1, 3 → 1: 4 + -2 = 2 is better, so dist[1] = 2. |
| 10 | L7 | round=1 edge=4 → 3 candidate=9 | Round 1, 4 → 3: 2 + 7 = 9 is not better than 4: no change. |
| 11 | L9 | round=1 dist=[0, 2, 7, 4, 2] | End of round 1: distances [0, 2, 7, 4, 2]. Paths with up to 1 edges are now correct. |
| 12 | L7 | round=2 edge=0 → 1 candidate=6 | Round 2, 0 → 1: 0 + 6 = 6 is not better than 2: no change. |
| 13 | L7 | round=2 edge=0 → 2 candidate=7 | Round 2, 0 → 2: 0 + 7 = 7 is not better than 7: no change. |
| 14 | L7 | round=2 edge=1 → 2 candidate=10 | Round 2, 1 → 2: 2 + 8 = 10 is not better than 7: no change. |
| 15 | L7 | round=2 edge=1 → 3 candidate=7 | Round 2, 1 → 3: 2 + 5 = 7 is not better than 4: no change. |
| 16 | L7 | round=2 u=1 v=4 dist[v]=-2 | Round 2, 1 → 4: 2 + -4 = -2 is better, so dist[4] = -2. |
| 17 | L7 | round=2 edge=2 → 3 candidate=4 | Round 2, 2 → 3: 7 + -3 = 4 is not better than 4: no change. |
| 18 | L7 | round=2 edge=2 → 4 candidate=16 | Round 2, 2 → 4: 7 + 9 = 16 is not better than -2: no change. |
| 19 | L7 | round=2 edge=3 → 1 candidate=2 | Round 2, 3 → 1: 4 + -2 = 2 is not better than 2: no change. |
| 20 | L7 | round=2 edge=4 → 3 candidate=5 | Round 2, 4 → 3: -2 + 7 = 5 is not better than 4: no change. |
| 21 | L9 | round=2 dist=[0, 2, 7, 4, -2] | End of round 2: distances [0, 2, 7, 4, -2]. Paths with up to 2 edges are now correct. |
| 22 | L7 | round=3 edge=0 → 1 candidate=6 | Round 3, 0 → 1: 0 + 6 = 6 is not better than 2: no change. |
| 23 | L7 | round=3 edge=0 → 2 candidate=7 | Round 3, 0 → 2: 0 + 7 = 7 is not better than 7: no change. |
| 24 | L7 | round=3 edge=1 → 2 candidate=10 | Round 3, 1 → 2: 2 + 8 = 10 is not better than 7: no change. |
| 25 | L7 | round=3 edge=1 → 3 candidate=7 | Round 3, 1 → 3: 2 + 5 = 7 is not better than 4: no change. |
| 26 | L7 | round=3 edge=1 → 4 candidate=-2 | Round 3, 1 → 4: 2 + -4 = -2 is not better than -2: no change. |
| 27 | L7 | round=3 edge=2 → 3 candidate=4 | Round 3, 2 → 3: 7 + -3 = 4 is not better than 4: no change. |
| 28 | L7 | round=3 edge=2 → 4 candidate=16 | Round 3, 2 → 4: 7 + 9 = 16 is not better than -2: no change. |
| 29 | L7 | round=3 edge=3 → 1 candidate=2 | Round 3, 3 → 1: 4 + -2 = 2 is not better than 2: no change. |
| 30 | L7 | round=3 edge=4 → 3 candidate=5 | Round 3, 4 → 3: -2 + 7 = 5 is not better than 4: no change. |
| 31 | L9 | round=3 | Round 3 changed nothing, so no later round can: stop early. |
| 32 | L12 | return=[0, 2, 7, 4, -2] relaxations=7 | No edge improves in the check pass: no negative cycle is reachable. Distances: [0, 2, 7, 4, -2]. Cost O(n · m). |
Practice ladder
Now try it yourself
Easy
- Network Delay Time
Medium
- Cheapest Flights Within K Stops
- Find the City With the Smallest Number of Neighbors at a Threshold Distance
Advanced
- Minimum Cost to Reach Destination in Time
- Number of Ways to Arrive at Destination
Key takeaways
- Bellman-Ford relaxes every edge, n - 1 times; time O(n · m); it handles negative edges.
- After k passes every shortest path of at most k edges is correct; stop early when a pass changes nothing.
- An improvement in pass n proves a negative cycle reachable from the source; step back along the improving edges to extract it.
- Vertices reachable from such a cycle have distance minus infinity; the others are unaffected.