Shortest Paths (Dijkstra and Bellman-Ford)

Find the cheapest route in a weighted graph, and know when negative edges break things.

In a weighted graph, the shortest path is the one with the smallest total weight, not the fewest edges. BFS no longer works; we need algorithms that take weights into account.

412153SACDB
A weighted directed graph (edge labels are distances)

Dijkstra's algorithm

Dijkstra's algorithm works when all edge weights are non-negative. It keeps a tentative distance for every vertex (∞ at first, 0 for the source) and repeatedly:

  1. picks the unvisited vertex u with the smallest tentative distance (a min-heap makes this fast),
  2. marks it final, and
  3. relaxes each edge (u, v): if dist[u] + w(u, v) < dist[v], update dist[v].

With a binary heap it runs in O((V + E) log V).

import java.util.*;

public class Main {
    public static void main(String[] args) {
        Map<String, Map<String, Integer>> g = new TreeMap<>();
        String[][] edges = {{"S", "A", "4"}, {"S", "B", "1"}, {"B", "A", "2"}, {"A", "C", "1"}, {"B", "C", "5"}, {"C", "D", "3"}};
        for (String[] e : edges) {
            g.computeIfAbsent(e[0], k -> new TreeMap<>()).put(e[1], Integer.parseInt(e[2]));
            g.computeIfAbsent(e[1], k -> new TreeMap<>());
        }

        Map<String, Integer> dist = new TreeMap<>();
        Map<String, String> prev = new HashMap<>();
        for (String v : g.keySet()) dist.put(v, Integer.MAX_VALUE);
        dist.put("S", 0);
        PriorityQueue<Object[]> pq = new PriorityQueue<>(Comparator.comparingInt(a -> (int) a[0]));
        pq.add(new Object[]{0, "S"});

        while (!pq.isEmpty()) {
            Object[] top = pq.poll();
            int d = (int) top[0];
            String u = (String) top[1];
            if (d > dist.get(u)) continue;                 // stale entry
            for (Map.Entry<String, Integer> e : g.get(u).entrySet()) {
                String v = e.getKey();
                int nd = d + e.getValue();
                if (nd < dist.get(v)) {                    // relax
                    dist.put(v, nd);
                    prev.put(v, u);
                    pq.add(new Object[]{nd, v});
                }
            }
        }

        System.out.println("distances from S: " + dist);
        LinkedList<String> path = new LinkedList<>();
        for (String v = "D"; v != null; v = prev.get(v)) path.addFirst(v);
        System.out.println("shortest path to D: " + String.join(" -> ", path));
    }
}
Output
distances from S: {A=3, B=1, C=4, D=7, S=0}
shortest path to D: S -> B -> A -> C -> D

The direct edge S → A costs 4, but going S → B → A costs only 1 + 2 = 3. Dijkstra discovers this because it finalises B (distance 1) before A.

Negative edges and Bellman-Ford

Dijkstra assumes that once a vertex is finalised, no cheaper path can appear later. A negative edge breaks that assumption. Bellman-Ford handles negative weights by simply relaxing every edge V − 1 times, in O(V·E) time. One more round that still improves a distance proves there is a negative cycle, in which case shortest paths are not defined.

public class Main {
    public static void main(String[] args) {
        String[] names = {"S", "A", "B", "C"};
        int[][] edges = {{0, 1, 4}, {0, 2, 5}, {1, 3, 3}, {2, 1, -3}, {3, 2, 2}};
        long INF = Long.MAX_VALUE / 2;
        long[] dist = {0, INF, INF, INF};

        for (int round = 1; round < names.length; round++)
            for (int[] e : edges)
                if (dist[e[0]] + e[2] < dist[e[1]]) dist[e[1]] = dist[e[0]] + e[2];

        boolean negativeCycle = false;
        for (int[] e : edges) if (dist[e[0]] + e[2] < dist[e[1]]) negativeCycle = true;

        for (int i = 0; i < names.length; i++) System.out.println(names[i] + ": " + dist[i]);
        System.out.println("negative cycle? " + negativeCycle);
    }
}
Output
S: 0
A: 2
B: 5
C: 5
negative cycle? false

Here S → B → A (5 − 3 = 2) beats the direct S → A (4). The cycle A → C → B → A has total weight 3 + 2 − 3 = 2 > 0, so it is not a negative cycle.

All pairs: Floyd-Warshall

To get the shortest distance between every pair of vertices, Floyd-Warshall uses dynamic programming. Let dist[i][j] start as the edge weight (∞ if there is no edge, 0 on the diagonal). Then, for each vertex k in turn, allow paths to go through k:

dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j])

Three nested loops give Θ(V³) time and Θ(V²) space. It works with negative edges (a negative value on the diagonal reveals a negative cycle).

public class Main {
    public static void main(String[] args) {
        final int INF = 1_000_000;
        int[][] dist = {
            {0, 3, INF, 7},
            {8, 0, 2, INF},
            {5, INF, 0, 1},
            {2, INF, INF, 0},
        };
        int n = dist.length;
        for (int k = 0; k < n; k++)
            for (int i = 0; i < n; i++)
                for (int j = 0; j < n; j++)
                    if (dist[i][k] + dist[k][j] < dist[i][j]) dist[i][j] = dist[i][k] + dist[k][j];

        for (int[] row : dist) {
            StringBuilder sb = new StringBuilder();
            for (int x : row) sb.append(String.format("%4d", x));
            System.out.println(sb);
        }
    }
}
Output
   0   3   5   6
   5   0   2   3
   3   6   0   1
   2   5   7   0
AlgorithmHandles negative edgesTimeUse for
BFSweights must all be equalO(V + E)unweighted graphs
Dijkstra (binary heap)noO((V + E) log V)non-negative weights
Bellman-Fordyes, and detects negative cyclesO(V·E)negative weights
Floyd-WarshallyesO(V³)all pairs of vertices

GATE-style practice questions

GATE-styleNAT · numerical answer

Dijkstra's algorithm is run from S on the graph at the top of this lesson. What is the shortest distance from S to C?

Show answer and explanationAnswer: 4

The cheapest route is S → B → A → C with cost 1 + 2 + 1 = 4. The direct-looking alternatives S → A → C (5) and S → B → C (6) are longer.

GATE-styleMCQ · one correct answer

Which algorithm should be used to find single-source shortest paths in a directed graph that has some negative edge weights but no negative cycles?

Show answer and explanationAnswer: B

Dijkstra can give wrong answers with negative edges because it finalises vertices too early. Bellman-Ford relaxes all edges V − 1 times and is correct whenever there is no negative cycle. Prim's algorithm finds a minimum spanning tree, not shortest paths.

GATE-styleMCQ · one correct answer

What is the time complexity of Dijkstra's algorithm with a binary heap on a connected graph with V vertices and E edges?

Show answer and explanationAnswer: B

Each edge relaxation may push into the heap (O(log V)) and each vertex is extracted once, giving O((V + E) log V), which is O(E log V) for a connected graph. With a simple array instead of a heap it is O(V²).

GATE-styleNAT · numerical answer

Floyd-Warshall is run on the 4-vertex graph in this lesson (vertices 0 to 3). What is the final shortest distance from vertex 1 to vertex 0?

Show answer and explanationAnswer: 5

The direct edge 1 → 0 costs 8, but 1 → 2 → 3 → 0 costs 2 + 1 + 2 = 5, which is the shortest.