Graphs · Advanced Graph Algorithms

Bellman-Ford Algorithm

Relaxing every edge in the graph, repeated exactly (V-1) times, so that a shortest path of at most (V-1) edges (the longest a simple path can be) is guaranteed to be found regardless of edge order - and running one more relaxation pass afterward to detect a negative-weight cycle, since a distance that can still improve after every simple path has been considered can only mean a cycle is being exploited.

  • 6 min read
  • 8 practice questions
  • 2 graded problems
On this page
  1. Description
  2. The approach
  3. A second flavor: capping the number of edges
  4. Where you'll see it
  5. Which tool for which job?
  6. Common mistakes
  7. Check yourself
  8. Key takeaways
  9. Quick revise
  10. Practice

Description

Negative edge weights come up naturally: refunds, energy regained going downhill, or exchange rates turned into costs with a logarithm. They break Dijkstra's rule that the closest node is final. They also allow a cycle whose total weight is negative, around which a "shortest" path could loop forever. Bellman-Ford relaxes every edge, round after round, until distances stop changing. It handles negative weights, reports a reachable negative cycle, and can cap the number of edges a path may use.

We'll go from simple to more technical:

  1. The pattern: V - 1 rounds of relaxing every edge, then one more to check for cycles.
  2. A second flavor: capping the number of edges (cheapest flight with at most k stops).
  3. Where it runs in production (distance-vector routing, arbitrage checks).
  4. What breaks it: stopping early, relaxing in place, and unreachable cycles.

The approach

Java

int[] bellmanFord(int n, int[][] edges, int src) {               // edges: {u, v, w}
    int[] dist = new int[n];
    Arrays.fill(dist, Integer.MAX_VALUE);
    dist[src] = 0;
    for (int round = 1; round <= n - 1; round++) {                 // V - 1 rounds
        boolean changed = false;
        for (int[] e : edges) {
            if (dist[e[0]] != Integer.MAX_VALUE && dist[e[0]] + e[2] < dist[e[1]]) {
                dist[e[1]] = dist[e[0]] + e[2];
                changed = true;
            }
        }
        if (!changed) break;                                       // a quiet round: everything is final
    }
    for (int[] e : edges) {                                        // one extra round
        if (dist[e[0]] != Integer.MAX_VALUE && dist[e[0]] + e[2] < dist[e[1]]) {
            return new int[]{Integer.MIN_VALUE};                   // still improving: negative cycle
        }
    }
    return dist;
}

Python

def bellman_ford(n: int, edges: list[list[int]], src: int) -> list[float] | None:   # edges: [u, v, w]
    dist = [float("inf")] * n
    dist[src] = 0
    for _ in range(n - 1):                               # V - 1 rounds
        changed = False
        for u, v, w in edges:
            if dist[u] + w < dist[v]:                    # inf + w is still inf: no guard needed
                dist[v] = dist[u] + w
                changed = True
        if not changed:
            break                                        # a quiet round: everything is final
    for u, v, w in edges:                                # one extra round
        if dist[u] + w < dist[v]:
            return None                                  # still improving: negative cycle
    return dist

Relaxing an edge means "if going through it is cheaper, take it". A shortest path that doesn't loop has at most V - 1 edges, and each round of relaxing every edge extends the correctly-computed prefix of that path by at least one more edge - whatever order the edges are listed in. So after V - 1 rounds every distance is right. If a further round still improves something, no finite shortest path exists: a negative cycle is being exploited. The != Integer.MAX_VALUE guard keeps unreachable nodes out of it (and avoids overflowing infinity). In Python, float("inf") makes the guard unnecessary: inf + w is still inf, which never beats anything.

A second flavor: capping the number of edges

"Cheapest flight with at most k stops" is Bellman-Ford stopped after k + 1 rounds - with one change. Relaxing in place lets a single round chain several new edges, so after round i distances may already use more than i edges. When the edge count matters, each round must read last round's values and write into a copy:

Java

for (int round = 0; round <= k; round++) {          // k stops = at most k + 1 flights
    int[] next = cost.clone();                       // read from cost, write to next
    for (int[] f : flights) {
        if (cost[f[0]] != INF && cost[f[0]] + f[2] < next[f[1]]) next[f[1]] = cost[f[0]] + f[2];
    }
    cost = next;
}

Python

for _ in range(k + 1):                   # k stops = at most k + 1 flights
    nxt = cost[:]                        # read from cost, write to a COPY (`nxt = cost` is the same list)
    for u, v, price in flights:
        if cost[u] + price < nxt[v]:
            nxt[v] = cost[u] + price
    cost = nxt

With flights 0->1 (100), 1->2 (100), 0->2 (500) and k = 0 (direct flights only), the answer is 500. In place, the round sets cost[1] = 100 and then immediately uses it for 1->2, reporting 200 - a two-flight route.

Where you'll see it

Distance-vector routing protocols (RIP) are distributed Bellman-Ford: each router repeatedly shares its distance table with neighbors and relaxes. Currency-arbitrage detection turns exchange rates into weights -log(rate), so a loop whose rates multiply to more than 1 becomes a negative cycle. Constraint systems of the form x_v - x_u <= w solve as shortest paths, with a negative cycle meaning "no solution".

Which tool for which job?

You needUse
Shortest paths with negative weightsBellman-Ford, V - 1 rounds with an early exit
Does a reachable negative cycle exist?one extra round: any improvement means yes
Which nodes have no shortest paththe nodes the extra round improves, plus everything reachable from them
Cheapest path with at most k edgesk rounds, each reading the previous round's copy
Negative weights, all pairsJohnson's algorithm (Bellman-Ford once, then Dijkstra)
Non-negative weightsDijkstra, which is faster

Common mistakes

MistakeWhat happensDo this instead
Fewer than V - 1 roundslong paths aren't finished yetV - 1 rounds, or stop after a round with no change
Relaxing in place with a hop limitanswers use too many edges (200 instead of 500)read last round, write a copy
No "unreachable" guard in JavaMAX_VALUE + (-5) makes an unreachable node look reachableskip edges from unreachable nodes
Exact float comparisons on log-weightsevery consistent market looks like arbitragecompare with a small tolerance
Treating a negative cycle as "all answers wrong"throws away valid distancesmark only the nodes it can reach

Check yourself

Try to answer before opening each one - recalling beats re-reading.

1. Why are V - 1 rounds always enough when there's no negative cycle?

Show answer

A shortest path without cycles visits each node at most once, so it has at most V - 1 edges. Each round of relaxing every edge makes at least one more edge of that path correct, whatever the edge order - so after V - 1 rounds the whole path, and its distance, are correct.

2. An extra round after V - 1 rounds still lowers a distance. What does that mean?

Show answer

A negative-weight cycle is reachable from the source. Without one, every distance would already be final after V - 1 rounds; continuing improvement means some path keeps getting cheaper by going around a cycle again.

3. Flights 0->1 (100), 1->2 (100), 0->2 (500), at most 0 stops from 0 to 2. Why does in-place relaxation report 200?

Show answer

In the single allowed round, 0->1 sets cost[1] = 100, and then 1->2 reads that fresh value and sets cost[2] = 200 - a two-flight route slipped into a one-flight round. Reading from last round's copy keeps round i to at most i edges, giving the correct 500.

Key takeaways

  • Bellman-Ford relaxes every edge V - 1 times; that's enough because a shortest path has at most V - 1 edges.
  • A round with no change means the distances are final - early exit is safe; a fixed smaller number of rounds is not.
  • One extra round that still improves something proves a reachable negative cycle.
  • For "at most k edges", relax from a copy of the previous round's distances.
  • O(V * E): slower than Dijkstra, but it handles negative weights.

Quick revise

The whole skill on one page, for the night before an interview.

This page puts the lesson and the deep dive together. Skim it before practice or an interview, and go back to the full lesson for anything you don't recognize.

Pick the tool

You needUse
Negative weightsBellman-Ford
Negative cycle?one extra round
Affected nodesimproved in that round, plus their reach
At most k edgesk rounds on copies
Faster on typical inputSPFA
Arbitrage-log(rate) plus a tolerance

Snippets worth knowing by heart

Java

for (int round = 0; round < n - 1; round++) {
    boolean changed = false;
    for (int[] e : edges)
        if (dist[e[0]] != INF && dist[e[0]] + e[2] < dist[e[1]]) { dist[e[1]] = dist[e[0]] + e[2]; changed = true; }
    if (!changed) break;
}

Python

x = improved_node                        # improved in the extra round
for _ in range(n):
    x = pred[x]                          # now x is on the cycle
cycle, y = [x], pred[x]
while y != x:
    cycle.append(y)
    y = pred[y]
cycle.reverse()

The traps

  • Too few rounds: paths longer than the rounds run aren't finished. Run V - 1 rounds, or stop after a quiet round.
  • In-place relaxation with a hop limit: answers use more edges than allowed. Read last round's values, write a copy.
  • No INF guard in Java: MAX_VALUE plus a negative weight looks reachable. Skip edges from unreachable nodes.
  • Exact float comparisons: rounding on log-weights makes every market look like arbitrage. Compare with a small tolerance.
  • Discarding every answer for one negative cycle: valid distances are lost. Mark only the nodes the cycle reaches.

Staff-level points, one line each

  • A cycle 1-2-3 ruined nodes [1, 2, 3, 4]; node 5 kept its distance, 7.
  • Following predecessors V times, then around once, gave the cycle GBP, USD, EUR, which turns 1 into 1.0125.
  • 200 consistent markets: an exact comparison found "arbitrage" in all 200, and a 1e-9 tolerance in none.
  • Java: with no guard, MAX_VALUE + (-5) made an unreachable node's distance 2147483642.
  • SPFA: 21,804 edge checks against Bellman-Ford's 100,000, with the same distances. Its worst case is unchanged.

Say it in an interview

  • "V - 1 rounds suffice because a shortest path has at most V - 1 edges, and I stop early on a quiet round."
  • "An improvement in round V means a reachable negative cycle."
  • "With a hop limit, each round reads last round's values."
  • "For arbitrage I use -log of the rates and compare with a tolerance."

Practice

This skill comes with 8 multiple-choice questions that tell you which misconception a wrong answer reveals and 2 graded problems. Your answers are graded and your mastery is tracked, so you'll know when the skill holds up. Running code and saving progress need a free account.

Practice Bellman-Ford Algorithm free

Bellman-Ford Algorithm

MediumProCoding problemJava · Python

Given a directed weighted graph with n nodes (labeled 0 to n-1, edge weights possibly negative) and a source node, return the shortest distance from the source to every node. If the graph has a negative-weight cycle reachable from the source, print CYCLE instead.

The full statement, the examples and the hidden test suite are part of AI Proof Engineers Pro.

Cheapest Flights Within K Stops

MediumProCoding problemJava · Python

Cities 0 to n-1 are linked by one-way flights u v price. Find the cheapest way to get from src to dst with at most k stops in between (so at most k + 1 flights), or return -1 if no such trip exists.

The full statement, the examples and the hidden test suite are part of AI Proof Engineers Pro.

Practice Bellman-Ford Algorithm free

No card needed. Lessons, practice questions and easy problems are free forever.

Free plan, no card · 7-day Pro trial · Cancel anytime