Graph · 21 of 43
Implement Bellman Ford Algorithm
Find the cheapest trip from home to every stop when some roads pay you back, and notice when a loop would pay you forever.
- Simple way
- time O(V!)
room O(V) - Clever way
- time O(V * E)
room O(V)
The problem, simply
You get V stops and one-way roads between them. Every road has a price. Some prices are negative: taking that road gives you money back. You start at stop src.
For every stop, find the cheapest total price to get there from src. A stop you cannot reach costs infinity. If there is a loop of roads whose prices add up to less than zero, you could drive around it forever and get richer each time, so no cheapest price exists. Report that instead.
A story to remember it
Picture it
Five stops and eight one-way roads. Two roads have negative prices: 0 to 1 costs -1 and 4 to 3 costs -3. The cheapest way to 3 is 0, 1, 4, 3 for -1 + 2 - 3 = -2.
- looking now
- path
Examples
The picture above
- In
V = 5, src = 0, edges = [[0,1,-1],[0,2,4],[1,2,3],[1,3,2],[1,4,2],[3,2,5],[3,1,1],[4,3,-3]]- Out
[0, -1, 2, -2, 1]
Going 0 to 1 to 2 costs -1 + 3 = 2, which beats the direct road of 4. The trip to 3 uses the -3 road.
A loop that pays you
- In
V = 3, src = 0, edges = [[0,1,1],[1,2,-1],[2,1,-1]]- Out
negative cycle
The loop 1, 2, 1 costs -1 + -1 = -2. Every lap makes the price lower, so there is no cheapest price.
A stop you cannot reach
- In
V = 3, src = 0, edges = [[0,1,5]]- Out
[0, 5, INF]
No road leads to stop 2, so its price stays infinity. GeeksforGeeks prints 100000000 for it.
No negative roads
- In
V = 3, src = 0, edges = [[0,1,4],[0,2,1],[2,1,2]]- Out
[0, 3, 1]
Bellman Ford still works: 0 to 2 to 1 costs 1 + 2 = 3, which beats the direct 4.
Way 1: the simple way
Try every route. From the source, walk every road to a stop you are not already on, and keep going. Each time you arrive somewhere, compare the price you paid with the best price written there.
- Start at
srcwith price 0. - At a stop, write down the price if it is the best so far.
- Walk every road to a stop that is not already on your current route, adding its price.
- When you return, take the stop off your route so other routes may use it.
import java.util.*;
class Solution {
static final int INF = Integer.MAX_VALUE;
public int[] cheapest(int V, int[][] edges, int src) {
List<List<int[]>> adj = new ArrayList<>();
for (int i = 0; i < V; i++) adj.add(new ArrayList<>());
for (int[] e : edges) adj.get(e[0]).add(new int[]{e[1], e[2]});
int[] best = new int[V];
Arrays.fill(best, INF);
walk(adj, src, 0, new boolean[V], best);
return best;
}
private void walk(List<List<int[]>> adj, int u, int paid, boolean[] onRoute, int[] best) {
best[u] = Math.min(best[u], paid);
onRoute[u] = true;
for (int[] road : adj.get(u)) {
if (!onRoute[road[0]]) walk(adj, road[0], paid + road[1], onRoute, best);
}
onRoute[u] = false; // let other routes pass through u
}
}How long it takes: the number of routes that never repeat a stop can be about V!, so O(V!). Extra room: the route and the recursion, O(V). It also cannot tell you about a loop that pays you, because it never walks a loop.
Way 2: the clever way
Fix the sticky notes in rounds. Checking one road and fixing the note at its end is called relaxing the road.
- Set
dist[src] = 0and every otherdistto infinity. - Repeat
V - 1times: go through every roadu > vwith pricew. Ifdist[u]is known anddist[u] + w < dist[v], setdist[v] = dist[u] + w. - If a whole round changes nothing, stop early.
- Do one more round. If any road can still make a note cheaper, there is a negative loop.
Why V - 1 rounds? After round 1, every stop whose best route uses 1 road has its final price. After round 2, every stop whose best route uses 2 roads, and so on. A best route visits at most V stops, so it has at most V - 1 roads.
Relax every road, round after round
01 / 11
Start: the source 0 costs 0 to reach. Every other stop costs infinity for now, because we have not found any way there yet.
- round
- 0
- looking now
- checking
- seen
- answer
- rejected
- just added
import java.util.*;
class Solution {
static final int INF = Integer.MAX_VALUE;
// returns null when there is a loop that pays you
public int[] bellmanFord(int V, int[][] edges, int src) {
int[] dist = new int[V];
Arrays.fill(dist, INF);
dist[src] = 0;
for (int round = 1; round <= V - 1; round++) {
boolean changed = false;
for (int[] e : edges) {
int u = e[0], v = e[1], w = e[2];
if (dist[u] != INF && dist[u] + w < dist[v]) { // relax the road
dist[v] = dist[u] + w;
changed = true;
}
}
if (!changed) break; // nothing moved, so nothing will
}
for (int[] e : edges) { // one extra round to catch a money loop
int u = e[0], v = e[1], w = e[2];
if (dist[u] != INF && dist[u] + w < dist[v]) return null;
}
return dist;
}
}How long it takes: up to V - 1 rounds, each checking all E roads, O(V * E). Extra room: the dist array, O(V).
Watch out for
- Running Dijkstra on negative roads. Dijkstra locks a stop the first time it pops from the heap, and a later negative road can make it cheaper. Bellman Ford never locks anything.
- Skipping the extra round. Without it, a negative loop quietly gives wrong prices.
- Undirected graphs with a negative edge. A two-way road with price -2 is already a loop of -4, so Bellman Ford will report a negative cycle.
- The order of roads changes how many rounds you need, but not the final answer. In the animation, the edge list order let round 1 finish everything.
What interviewers ask next
- Cheapest flight with at most
kstops (LeetCode 787)? Run exactlyk + 1rounds, and in each round read prices from a copy of the previous round, so one round adds at most one road. - Which stops are hurt by a negative loop? After the extra round, every stop reachable from a road that still relaxes has price minus infinity. Mark them with a search from those stops.
- Can you speed it up? The queue version (SPFA) only rechecks roads leaving stops whose price just changed. It is often fast but still
O(V * E)in the worst case. - Bellman Ford or Dijkstra? With no negative roads, Dijkstra is faster at
O(E log V). With negative roads, or when you must detect a negative loop, use Bellman Ford. - Currency exchange: can you make money trading in a circle? Use price
-log(rate)on each road. A circle of trades that makes money is a negative loop.
Remember this
Grown-up words: stops are vertices, roads are directed weighted edges, the sticky notes are the distance array, fixing a note is relaxation, the loop that pays you is a negative weight cycle, and the method is the Bellman Ford algorithm, a single source shortest path algorithm.