Skip to content
Home DSA Bellman-Ford Algorithm — Shortest Paths with Negative Weights

Bellman-Ford Algorithm — Shortest Paths with Negative Weights

Where developers are forged. · Structured learning · Free forever.
📍 Part of: Graphs → Topic 5 of 17
Master the Bellman-Ford algorithm: find shortest paths in graphs with negative weight edges and detect negative weight cycles in O(VE) time.
🔥 Advanced — solid DSA foundation required
In this tutorial, you'll learn
Master the Bellman-Ford algorithm: find shortest paths in graphs with negative weight edges and detect negative weight cycles in O(VE) time.
  • Bellman-Ford relaxes all edges V-1 times, finding shortest paths even with negative weights in O(VE).
  • A Vth relaxation pass that still improves a distance proves a negative cycle exists.
  • Dijkstra is faster but only correct for non-negative weights; Bellman-Ford handles negative edges.
✦ Plain-English analogy ✦ Real code with output ✦ Interview questions
Quick Answer

Imagine you're planning a road trip and some roads give you fuel credits (negative cost) instead of costing you money. Dijkstra's algorithm would panic — it assumes every road costs you something. Bellman-Ford, on the other hand, is the calm navigator who says 'let me check every single road, multiple times, just to be sure I've found the cheapest route — even if some roads pay YOU to drive them.' It also warns you if there's a loop of fuel-credit roads that would let you drive forever for free, which would make any shortest-path answer meaningless.

Financial trading systems use graph algorithms to find arbitrage opportunities — sequences of currency exchanges that yield profit. GPS routing engines handle toll roads, ferry routes, and restricted zones. Network packet routing protocols like RIP (Routing Information Protocol) propagate cost updates across distributed nodes. In every one of these systems, the edge weights on a graph can be negative. That single reality invalidates Dijkstra's algorithm entirely, and it's the reason Bellman-Ford exists.

The core problem Bellman-Ford solves is single-source shortest paths in a weighted directed graph where edge weights can be negative. Dijkstra's greedy approach assumes that once a node is 'settled' its shortest distance is final — a guarantee that collapses the moment a future negative edge could have produced a cheaper path. Bellman-Ford abandons the greedy assumption and instead performs exhaustive relaxation: it checks every edge, repeatedly, until it's mathematically certain no cheaper path exists. As a bonus, it can detect negative-weight cycles — loops in the graph where traversing the cycle keeps reducing your total cost to negative infinity, making shortest paths undefined.

By the end of this article you'll understand exactly why Bellman-Ford performs V-1 relaxation passes and not V or V-2, how to implement it correctly in Java with proper negative-cycle detection, where it outperforms Dijkstra in practice, what the subtle off-by-one bugs look like in interview code, and how production systems like distributed routing protocols derive their design directly from this algorithm's properties.

What is Bellman-Ford? — Plain English

Bellman-Ford finds the shortest path from a source node to all other nodes in a weighted graph, even when some edge weights are negative. Dijkstra's algorithm fails with negative edges because it assumes that once a node is finalized, its distance cannot improve — negative edges can always make a path shorter. Bellman-Ford handles this by relaxing all edges V-1 times. Why V-1? The shortest path between any two nodes in a graph with V nodes uses at most V-1 edges (otherwise it would visit a node twice, forming a cycle). After V-1 relaxation passes, all shortest paths are found. A V-th pass that still relaxes an edge signals a negative cycle — a loop whose total weight is negative, making the shortest path undefined.

How Bellman-Ford Works — Step by Step

  1. Initialize dist[source] = 0, dist[all others] = infinity.
  2. Repeat V-1 times:
  3. a. For each edge (u, v, weight) in the edge list:
  4. b. If dist[u] + weight < dist[v]: relax → dist[v] = dist[u] + weight.
  5. Negative cycle detection: do one more pass over all edges.
  6. a. If any edge (u,v,w) still satisfies dist[u] + w < dist[v], a negative cycle exists.
  7. Return the dist array (and predecessors if path reconstruction is needed).

Worked Example — 5-Node Graph with Negative Edge

Graph edges (u,v,weight): (0,1,4),(0,2,5),(1,2,-3),(1,3,6),(2,4,2),(3,4,1),(4,3,-2). Source=0.

Initial: dist=[0,inf,inf,inf,inf].

Pass 1 (relax all edges)
  • (0,1,4): dist[1]=min(inf,0+4)=4.
  • (0,2,5): dist[2]=min(inf,0+5)=5.
  • (1,2,-3): dist[2]=min(5,4-3)=1.
  • (1,3,6): dist[3]=min(inf,4+6)=10.
  • (2,4,2): dist[4]=min(inf,1+2)=3.
  • (3,4,1): dist[4]=min(3,10+1)=3 (no change).
  • (4,3,-2): dist[3]=min(10,3-2)=1. dist after pass 1: [0,4,1,1,3].
Pass 2
  • (1,3,6): dist[3]=min(1,4+6)=1 no change.
  • (4,3,-2): dist[3]=min(1,3-2)=1 no change.
  • All others: no improvement. dist unchanged.

Passes 3,4: no changes. Final dist=[0,4,1,1,3].

Implementation

Bellman-Ford iterates over the edge list V-1 times, relaxing each edge. The edge list representation (list of (u,v,weight) tuples) is simpler to use than adjacency lists for this algorithm. The V-th pass detects negative cycles. Time complexity O(VE) makes it slower than Dijkstra's O((V+E) log V), but it handles negative weights. Space complexity is O(V) for the distance array plus O(E) for the edge list.

bellman_ford.py · PYTHON
123456789101112131415161718192021222324252627282930313233
def bellman_ford(num_vertices, edges, source):
    """
    edges: list of (u, v, weight)
    Returns (dist, has_negative_cycle)
    """
    INF = float('inf')
    dist = [INF] * num_vertices
    dist[source] = 0

    # Relax all edges V-1 times
    for _ in range(num_vertices - 1):
        updated = False
        for u, v, w in edges:
            if dist[u] != INF and dist[u] + w < dist[v]:
                dist[v] = dist[u] + w
                updated = True
        if not updated:  # Early exit if no relaxation occurred
            break

    # Detect negative cycles (V-th pass)
    has_neg_cycle = False
    for u, v, w in edges:
        if dist[u] != INF and dist[u] + w < dist[v]:
            has_neg_cycle = True
            break

    return dist, has_neg_cycle

# Example: 4-node graph
edges = [(0,1,4),(0,2,5),(1,2,-3),(1,3,6),(2,3,2)]
dist, neg = bellman_ford(4, edges, 0)
print(dist)   # [0, 4, 1, 3]
print(neg)    # False
▶ Output
[0, 4, 1, 3]
False
Feature / AspectBellman-FordDijkstra
Time ComplexityO(V × E)O((V + E) log V) with binary heap
Space ComplexityO(V) for distances + O(E) for edge listO(V) for distances + O(V) for priority queue
Negative Edge Weights✅ Fully supported❌ Produces incorrect results
Negative Cycle Detection✅ Built-in with V-th pass❌ Not supported — undefined behaviour
Graph TypeDirected (adaptable to undirected)Directed and undirected
Implementation ComplexitySimple — flat edge-list loopModerate — requires priority queue
Distributed Systems Use✅ Foundation of RIP, distance-vector routing❌ Requires global state — not distributed-friendly
Dense Graph PerformancePoor — O(V³) on complete graphGood — O(V² log V) on complete graph
Sparse Graph PerformanceGood when E ≈ VExcellent when E ≈ V
All-Pairs Shortest PathsO(V² × E) — very slowBetter via Johnson's Algorithm (run BF once, Dijkstra V times)

🎯 Key Takeaways

  • Bellman-Ford relaxes all edges V-1 times, finding shortest paths even with negative weights in O(VE).
  • A Vth relaxation pass that still improves a distance proves a negative cycle exists.
  • Dijkstra is faster but only correct for non-negative weights; Bellman-Ford handles negative edges.

⚠ Common Mistakes to Avoid

    Running exactly V passes instead of V-1 for relaxation, then having no V-th pass for detection — The algorithm appears to work on graphs without negative cycles, but silently skips negative-cycle detection entirely. Any graph with a negative cycle returns plausible-looking but meaningless distances. Fix: use a loop `for (pass = 0; pass < numVertices - 1; pass++)` for relaxation, then a separate loop after it for detection — never conflate the two.
    Fix

    use a loop for (pass = 0; pass < numVertices - 1; pass++) for relaxation, then a separate loop after it for detection — never conflate the two.

    Using Integer.MAX_VALUE as the initial distance sentinel and adding a positive weight to it — This causes integer overflow, wrapping around to a large negative number. Suddenly every edge looks like it relaxes, and the output is garbage with no error thrown. Fix: use Long.MAX_VALUE with a `long[]` distance array, or add an explicit guard `if (distanceTo[source] != Long.MAX_VALUE)` before attempting relaxation.
    Fix

    use Long.MAX_VALUE with a long[] distance array, or add an explicit guard if (distanceTo[source] != Long.MAX_VALUE) before attempting relaxation.

    Declaring 'no negative cycle' simply because no relaxation fires on the V-th pass from your specific source — If the negative cycle is in a disconnected component or unreachable from your source, Bellman-Ford won't see it. The correct fix for full-graph negative-cycle detection is to add a dummy super-source vertex with zero-weight edges to every other vertex, run Bellman-Ford from that super-source, and perform the V-th pass check. This is exactly what Johnson's Algorithm does as its preprocessing step.
    Fix

    for full-graph negative-cycle detection is to add a dummy super-source vertex with zero-weight edges to every other vertex, run Bellman-Ford from that super-source, and perform the V-th pass check. This is exactly what Johnson's Algorithm does as its preprocessing step.

Interview Questions on This Topic

  • QWhy does Bellman-Ford need V-1 iterations?
  • QHow does Bellman-Ford detect negative cycles?
  • QWhat is the time complexity of Bellman-Ford?

Frequently Asked Questions

Why does Bellman-Ford need exactly V-1 passes?

The shortest path with no repeated vertices uses at most V-1 edges. Each pass of Bellman-Ford guarantees that all shortest paths using at most k edges are correctly computed after k passes. After V-1 passes, all shortest paths (which use at most V-1 edges) are found. A Vth pass that still finds improvement implies a cycle in the path, which must be a negative cycle.

When should I use Bellman-Ford instead of Dijkstra?

Use Bellman-Ford when: (1) the graph has negative weight edges, (2) you need to detect negative cycles, or (3) the graph is represented as an edge list. Use Dijkstra when all weights are non-negative and you need faster performance O((V+E) log V). In most competitive programming and interview problems, Dijkstra is preferred unless negative weights are specified.

Can Bellman-Ford find the shortest path in a graph with a negative cycle?

No. If a negative cycle is reachable from the source, the shortest path to some nodes is negative infinity (you can loop the negative cycle infinitely). Bellman-Ford detects the negative cycle but cannot return meaningful distances for nodes reachable through it. The algorithm returns a flag indicating cycle presence.

🔥
Naren Founder & Author

Developer and founder of TheCodeForge. I built this site because I was tired of tutorials that explain what to type without explaining why it works. Every article here is written to make concepts actually click.

← PreviousDijkstra Shortest Path AlgorithmNext →Floyd-Warshall Algorithm
Forged with 🔥 at TheCodeForge.io — Where Developers Are Forged