Card 05/ 11

CodeDifficulty: Intermediate5 min

Bellman-Ford in Python, Java and TypeScript

Passes over an edge list, stopping early when a pass changes nothing, and a final check for a negative cycle.

Bellman-Ford needs no adjacency lists and no queue: a list of edges, a distance per vertex, and two loops. The listings below run on the topic's graph and on the same graph with E->C of weight −6 added, and print the distances, or the negative cycle, with the number of passes run.

Bellman-Ford with an early stop and a negative-cycle check, in each language

bellman_code.pypy
import math


def bellman_ford(vertices, edges, source):
    distance = {vertex: math.inf for vertex in vertices}
    distance[source] = 0
    passes = 0
    for _ in range(len(vertices) - 1):
        passes += 1
        changed = False
        for u, v, weight in edges:
            if distance[u] + weight < distance[v]:
                distance[v] = distance[u] + weight
                changed = True
        if not changed:
            break
    for u, v, weight in edges:
        if distance[u] + weight < distance[v]:
            return None, passes
    return distance, passes


VERTICES = ["A", "B", "C", "D", "E"]
EDGES = [("A", "B", 2), ("A", "C", 7), ("B", "C", 3), ("B", "D", 8), ("C", "D", 2), ("C", "E", 6), ("D", "E", 1)]

for name, edges in (("topic graph", EDGES), ("with E->C -6", EDGES + [("E", "C", -6)])):
    distance, passes = bellman_ford(VERTICES, edges, "A")
    if distance is None:
        print(f"{name}: negative cycle, found after {passes} passes")
    else:
        print(f"{name}: {', '.join(f'{v} {distance[v]}' for v in VERTICES)} after {passes} passes")
OutputRan with python:3.14.8-slim-trixie (Python 3.14.8)
topic graph: A 0, B 2, C 5, D 7, E 8 after 2 passes
with E->C -6: negative cycle, found after 4 passes
BellmanCode.javajava
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class BellmanCode {
    record Edge(String from, String to, int weight) {}

    record Result(Map<String, Integer> distance, int passes, boolean negativeCycle) {}

    static final int INF = Integer.MAX_VALUE;

    static boolean relaxes(Map<String, Integer> distance, Edge edge) {
        int from = distance.get(edge.from());
        return from != INF && from + edge.weight() < distance.get(edge.to());
    }

    static Result bellmanFord(List<String> vertices, List<Edge> edges, String source) {
        Map<String, Integer> distance = new HashMap<>();
        for (String vertex : vertices) distance.put(vertex, INF);
        distance.put(source, 0);
        int passes = 0;
        for (int round = 0; round < vertices.size() - 1; round++) {
            passes++;
            boolean changed = false;
            for (Edge edge : edges) {
                if (relaxes(distance, edge)) {
                    distance.put(edge.to(), distance.get(edge.from()) + edge.weight());
                    changed = true;
                }
            }
            if (!changed) break;
        }
        boolean negativeCycle = edges.stream().anyMatch(edge -> relaxes(distance, edge));
        return new Result(distance, passes, negativeCycle);
    }

    public static void main(String[] args) {
        List<String> vertices = List.of("A", "B", "C", "D", "E");
        List<Edge> edges = List.of(new Edge("A", "B", 2), new Edge("A", "C", 7), new Edge("B", "C", 3), new Edge("B", "D", 8),
                new Edge("C", "D", 2), new Edge("C", "E", 6), new Edge("D", "E", 1));
        List<Edge> withCycle = new ArrayList<>(edges);
        withCycle.add(new Edge("E", "C", -6));
        Map<String, List<Edge>> graphs = Map.of("topic graph", edges, "with E->C -6", withCycle);
        for (String name : List.of("topic graph", "with E->C -6")) {
            Result result = bellmanFord(vertices, graphs.get(name), "A");
            if (result.negativeCycle()) {
                System.out.println(name + ": negative cycle, found after " + result.passes() + " passes");
            } else {
                List<String> parts = new ArrayList<>();
                for (String vertex : vertices) parts.add(vertex + " " + result.distance().get(vertex));
                System.out.println(name + ": " + String.join(", ", parts) + " after " + result.passes() + " passes");
            }
        }
    }
}
OutputRan with eclipse-temurin:25.0.4.1_1-jdk-noble (Java 25.0.4.1)
topic graph: A 0, B 2, C 5, D 7, E 8 after 2 passes
with E->C -6: negative cycle, found after 4 passes
bellman_code.tsts
type Edge = [string, string, number];

function bellmanFord(vertices: string[], edges: Edge[], source: string): { distance: Map<string, number> | null; passes: number } {
  const distance = new Map<string, number>(vertices.map((v) => [v, Infinity]));
  distance.set(source, 0);
  let passes = 0;
  for (let round = 0; round < vertices.length - 1; round++) {
    passes++;
    let changed = false;
    for (const [u, v, weight] of edges) {
      if (distance.get(u)! + weight < distance.get(v)!) {
        distance.set(v, distance.get(u)! + weight);
        changed = true;
      }
    }
    if (!changed) break;
  }
  const negativeCycle = edges.some(([u, v, weight]) => distance.get(u)! + weight < distance.get(v)!);
  return { distance: negativeCycle ? null : distance, passes };
}

const vertices = ["A", "B", "C", "D", "E"];
const edges: Edge[] = [["A", "B", 2], ["A", "C", 7], ["B", "C", 3], ["B", "D", 8], ["C", "D", 2], ["C", "E", 6], ["D", "E", 1]];
const graphs: [string, Edge[]][] = [["topic graph", edges], ["with E->C -6", [...edges, ["E", "C", -6]]]];
for (const [name, graphEdges] of graphs) {
  const { distance, passes } = bellmanFord(vertices, graphEdges, "A");
  if (distance === null) console.log(`${name}: negative cycle, found after ${passes} passes`);
  else console.log(`${name}: ${vertices.map((v) => `${v} ${distance.get(v)}`).join(", ")} after ${passes} passes`);
}
OutputRan with node:24.22.0-trixie-slim (Node 24.22.0), TypeScript 5.9.3
topic graph: A 0, B 2, C 5, D 7, E 8 after 2 passes
with E->C -6: negative cycle, found after 4 passes

All three printed the same two lines. The highlighted lines are the bound of V − 1 passes, the early stop when a pass changes nothing, and the extra pass that looks for a negative cycle.

The topic's graph stopped after 2 passes, where the bound allowed 4. The edges are listed in the order A->B, A->C, B->C and so on, so each edge's tail is set before the edge is reached and the first pass sets every distance; the second pass changes nothing and ends the loop. With the edges reversed the same program would run all four passes, as the trace on Bellman-Ford: Relaxing Every Edge V − 1 Times shows.

The graph with the negative cycle used all four passes, because every pass lowered something on the cycle, and the check still found an edge to relax.

Infinity is math.inf in Python and Infinity in TypeScript, and both stay infinite when a weight is added, so an unreached vertex can never relax an edge. Java's Integer.MAX_VALUE plus a weight would overflow to a negative number, so relaxes checks for it first.