The problem is to find the shortest paths between every pair of vertices in a given weighted directed Graph and weights may be negative. We have discussed Floyd Warshall Algorithm for this problem. Â The time complexity of the Floyd Warshall Algorithm is Î(V3).Â
Using Johnson's algorithm, we can find all pair shortest paths in O(V2log V + VE) time. Johnsonâs algorithm uses both Dijkstra and Bellman-Ford as subroutines. If we apply Dijkstra's Single Source shortest path algorithm for every vertex, considering every vertex as the source, we can find all pair shortest paths in O(V*(V + E) * Log V) time.Â
So using Dijkstra's single-source shortest path seems to be a better option than Floyd Warshall's Algorithm , but the problem with Dijkstra's algorithm is, that it doesn't work for negative weight edge. The idea of Johnson's algorithm is to re-weight all edges and make them all positive, then apply Dijkstra's algorithm for every vertex.Â
How to transform a given graph into a graph with all non-negative weight edges?Â
One may think of a simple approach of finding the minimum weight edge and adding this weight to all edges. Unfortunately, this doesn't work as there may be a different number of edges in different paths (See this for an example). If there are multiple paths from a vertex u to v, then all paths must be increased by the same amount, so that the shortest path remains the shortest in the transformed graph. The idea of Johnson's algorithm is to assign a weight to every vertex. Let the weight assigned to vertex u be h[u].Â
We reweight edges using vertex weights. For example, for an edge (u, v) of weight w(u, v), the new weight becomes w(u, v) + h[u] - h[v]. The great thing about this reweighting is, that all set of paths between any two vertices is increased by the same amount and all negative weights become non-negative. Consider any path between two vertices s and t, the weight of every path is increased by h[s] - h[t], and all h[] values of vertices on the path from s to t cancel each other.Â
How do we calculate h[] values?Â
Bellman-Ford algorithm is used for this purpose. Following is the complete algorithm. A new vertex is added to the graph and connected to all existing vertices. The shortest distance values from the new vertex to all existing vertices are h[] values.Â
Algorithm:Â
- Let the given graph be G. Add a new vertex s to the graph, add edges from the new vertex to all vertices of G. Let the modified graph be G'.Â
- Run the Bellman-Ford algorithm on G' with s as the source. Let the distances calculated by Bellman-Ford be h[0], h[1], .. h[V-1]. If we find a negative weight cycle, then return. Note that the negative weight cycle cannot be created by new vertex s as there is no edge to s. All edges are from s.Â
- Reweight the edges of the original graph. For each edge (u, v), assign the new weight as "original weight + h[u] - h[v]".Â
- Remove the added vertex s and run Dijkstra's algorithm for every vertex.Â
How does the transformation ensure nonnegative weight edges?Â
The following property is always true about h[] values as they are the shortest distances.
h[v] <= h[u] + w(u, v)
The property simply means that the shortest distance from s to v must be smaller than or equal to the shortest distance from s to u plus the weight of the edge (u, v). The new weights are w(u, v) + h[u] - h[v]. The value of the new weights must be greater than or equal to zero because of the inequality "h[v] <= h[u] + w(u, v)".Â
Example: Let us consider the following graph.Â

 We add a source s and add edges from s to all vertices of the original graph. In the following diagram s is 4.Â
Â
Â
We calculate the shortest distances from 4 to all other vertices using Bellman-Ford algorithm. The shortest distances from 4 to 0, 1, 2 and 3 are 0, -5, -1 and 0 respectively, i.e., h[] = {0, -5, -1, 0}. Once we get these distances, we remove the source vertex 4 and reweight the edges using following formula. w(u, v) = w(u, v) + h[u] - h[v].
Â
Â
Since all weights are positive now, we can run Dijkstra's shortest path algorithm for every vertex as the source.Â
#include <iostream>
#include <vector>
#include <limits>
#include <algorithm>
#include <queue>
#include <iomanip> // For matrix formatting
using namespace std;
const long long INF = 1e15;
struct Edge {
int to;
int weight;
};
typedef pair<long long, int> pii;
// Dijkstra's algorithm for non-negative edge weights
vector<long long> Dijkstra(int V, const vector<vector<Edge>>& adj, int src) {
vector<long long> dist(V, INF);
priority_queue<pii, vector<pii>, greater<pii>> pq;
dist[src] = 0;
pq.push({0, src});
while (!pq.empty()) {
long long d = pq.top().first;
int u = pq.top().second; // Fixed: added .top()
pq.pop();
if (d > dist[u]) continue;
for (auto& edge : adj[u]) {
if (dist[u] + edge.weight < dist[edge.to]) {
dist[edge.to] = dist[u] + edge.weight;
pq.push({dist[edge.to], edge.to});
}
}
}
return dist;
}
// Bellman-Ford to find h[] and detect negative cycles
vector<long long> BellmanFord(int V, const vector<vector<int>>& edges, bool& hasCycle) {
vector<long long> h(V + 1, INF);
h[V] = 0;
vector<vector<int>> all_edges = edges;
for (int i = 0; i < V; i++) all_edges.push_back({V, i, 0});
for (int i = 0; i < V; i++) {
for (auto& e : all_edges) {
if (h[e[0]] != INF && h[e[0]] + e[2] < h[e[1]]) {
h[e[1]] = h[e[0]] + e[2];
}
}
}
hasCycle = false;
for (auto& e : all_edges) {
if (h[e[0]] != INF && h[e[0]] + e[2] < h[e[1]]) {
hasCycle = true;
return {};
}
}
h.pop_back();
return h;
}
void JohnsonAlgorithm(int V, const vector<vector<int>>& edgeList) {
bool hasCycle;
vector<long long> h = BellmanFord(V, edgeList, hasCycle);
if (hasCycle) {
cout << "The graph contains a negative weight cycle. Algorithm cannot proceed." << endl;
return;
}
// Reweight edges to be non-negative
vector<vector<Edge>> adj(V);
for (auto& e : edgeList) {
int u = e[0], v = e[1], w = e[2];
adj[u].push_back({v, (int)(w + h[u] - h[v])});
}
vector<vector<long long>> resultMatrix(V, vector<long long>(V));
// Run Dijkstra for every vertex
for (int s = 0; s < V; s++) {
vector<long long> d_prime = Dijkstra(V, adj, s);
for (int v = 0; v < V; v++) {
if (d_prime[v] == INF) resultMatrix[s][v] = INF;
else resultMatrix[s][v] = d_prime[v] + h[v] - h[s];
}
}
// Display the Matrix
cout << "Shortest Distance Matrix:\n";
for (int i = 0; i < V; i++) {
for (int j = 0; j < V; j++) {
if (resultMatrix[i][j] >= INF/2) cout << setw(7) << "INF";
else cout << setw(7) << resultMatrix[i][j];
}
cout << endl;
}
}
int main() {
int V = 4;
vector<vector<int>> edgeList = {
{0, 1, -5}, {0, 2, 2}, {0, 3, 3},
{1, 2, 4},
{2, 3, 1}
};
JohnsonAlgorithm(V, edgeList);
return 0;
}
import java.util.*;
class Edge {
int to;
int weight;
Edge(int to, int weight) {
this.to = to;
this.weight = weight;
}
}
class Pair implements Comparable<Pair> {
long dist;
int vertex;
Pair(long dist, int vertex) {
this.dist = dist;
this.vertex = vertex;
}
public int compareTo(Pair other) {
return Long.compare(this.dist, other.dist);
}
}
public class GFG {
private static final long INF = 1000000000000000L;
private static List<Long> dijkstra(int V, List<List<Edge>> adj, int src) {
List<Long> dist = new ArrayList<>(Collections.nCopies(V, INF));
PriorityQueue<Pair> pq = new PriorityQueue<>();
dist.set(src, 0L);
pq.add(new Pair(0, src));
while (!pq.isEmpty()) {
Pair p = pq.poll();
long d = p.dist;
int u = p.vertex;
if (d > dist.get(u)) continue;
for (Edge edge : adj.get(u)) {
if (dist.get(u) + edge.weight < dist.get(edge.to)) {
dist.set(edge.to, dist.get(u) + edge.weight);
pq.add(new Pair(dist.get(edge.to), edge.to));
}
}
}
return dist;
}
private static List<Long> bellmanFord(int V, List<int[]> edges, boolean[] hasCycle) {
List<Long> h = new ArrayList<>(Collections.nCopies(V + 1, INF));
h.set(V, 0L);
List<int[]> all_edges = new ArrayList<>(edges);
for (int i = 0; i < V; i++) all_edges.add(new int[] {V, i, 0});
for (int i = 0; i < V; i++) {
for (int[] e : all_edges) {
if (h.get(e[0])!= INF && h.get(e[0]) + e[2] < h.get(e[1])) {
h.set(e[1], h.get(e[0]) + e[2]);
}
}
}
hasCycle[0] = false;
for (int[] e : all_edges) {
if (h.get(e[0])!= INF && h.get(e[0]) + e[2] < h.get(e[1])) {
hasCycle[0] = true;
return new ArrayList<>();
}
}
h.remove(h.size() - 1);
return h;
}
public static void johnsonAlgorithm(int V, List<int[]> edgeList) {
boolean[] hasCycle = {false};
List<Long> h = bellmanFord(V, edgeList, hasCycle);
if (hasCycle[0]) {
System.out.println("The graph contains a negative weight cycle. Algorithm cannot proceed.");
return;
}
List<List<Edge>> adj = new ArrayList<>();
for (int i = 0; i < V; i++) adj.add(new ArrayList<>());
for (int[] e : edgeList) {
int u = e[0], v = e[1], w = e[2];
adj.get(u).add(new Edge(v, (int)(w + h.get(u) - h.get(v))));
}
long[][] resultMatrix = new long[V][V];
for (int s = 0; s < V; s++) {
List<Long> d_prime = dijkstra(V, adj, s);
for (int v = 0; v < V; v++) {
if (d_prime.get(v) == INF) resultMatrix[s][v] = INF;
else resultMatrix[s][v] = d_prime.get(v) + h.get(v) - h.get(s);
}
}
System.out.println("Shortest Distance Matrix:");
for (int i = 0; i < V; i++) {
for (int j = 0; j < V; j++) {
if (resultMatrix[i][j] >= INF / 2) System.out.printf("%7s", "INF");
else System.out.printf("%7d", resultMatrix[i][j]);
}
System.out.println();
}
}
public static void main(String[] args) {
int V = 4;
List<int[]> edgeList = Arrays.asList(
new int[] {0, 1, -5},
new int[] {0, 2, 2},
new int[] {0, 3, 3},
new int[] {1, 2, 4},
new int[] {2, 3, 1}
);
johnsonAlgorithm(V, edgeList);
}
}
import heapq
class Edge:
def __init__(self, to, weight):
self.to = to
self.weight = weight
class Pair:
def __init__(self, dist, vertex):
self.dist = dist
self.vertex = vertex
def __lt__(self, other):
return self.dist < other.dist
INF = 1000000000000000
def dijkstra(V, adj, src):
dist = [INF] * V
pq = []
heapq.heappush(pq, Pair(0, src))
dist[src] = 0
while pq:
p = heapq.heappop(pq)
d = p.dist
u = p.vertex
if d > dist[u]:
continue
for edge in adj[u]:
if dist[u] + edge.weight < dist[edge.to]:
dist[edge.to] = dist[u] + edge.weight
heapq.heappush(pq, Pair(dist[edge.to], edge.to))
return dist
def bellmanFord(V, edges, hasCycle):
h = [INF] * (V + 1)
h[V] = 0
all_edges = edges + [(V, i, 0) for i in range(V)]
for _ in range(V):
for u, v, w in all_edges:
if h[u]!= INF and h[u] + w < h[v]:
h[v] = h[u] + w
hasCycle[0] = False
for u, v, w in all_edges:
if h[u]!= INF and h[u] + w < h[v]:
hasCycle[0] = True
return []
del h[-1]
return h
def johnsonAlgorithm(V, edgeList):
hasCycle = [False]
h = bellmanFord(V, edgeList, hasCycle)
if hasCycle[0]:
print('The graph contains a negative weight cycle. Algorithm cannot proceed.')
return
adj = [[] for _ in range(V)]
for u, v, w in edgeList:
adj[u].append(Edge(v, w + h[u] - h[v]))
resultMatrix = [[INF] * V for _ in range(V)]
for s in range(V):
d_prime = dijkstra(V, adj, s)
for v in range(V):
if d_prime[v] == INF:
resultMatrix[s][v] = INF
else:
resultMatrix[s][v] = d_prime[v] + h[v] - h[s]
print('Shortest Distance Matrix:')
for row in resultMatrix:
print(' '.join('INF' if x >= INF // 2 else str(x) for x in row))
if __name__ == '__main__':
V = 4
edgeList = [
(0, 1, -5),
(0, 2, 2),
(0, 3, 3),
(1, 2, 4),
(2, 3, 1)
]
johnsonAlgorithm(V, edgeList)
'use strict';
class Edge {
constructor(to, weight) {
this.to = to;
this.weight = weight;
}
}
class Pii {
constructor(d, u) {
this.d = d;
this.u = u;
}
}
Pii.prototype.compareTo = function(other) {
return this.d < other.d;
};
const INF = 1000000000000000;
function Dijkstra(V, adj, src) {
let dist = new Array(V).fill(INF);
dist[src] = 0;
let pq = [new Pii(0, src)];
while (pq.length > 0) {
pq.sort((a, b) => a.d - b.d);
let top = pq.shift();
let d = top.d;
let u = top.u;
if (d > dist[u]) {
continue;
}
for (let edge of adj[u]) {
if (dist[u] + edge.weight < dist[edge.to]) {
dist[edge.to] = dist[u] + edge.weight;
pq.push(new Pii(dist[edge.to], edge.to));
}
}
}
return dist;
}
function BellmanFord(V, edges, hasCycle) {
let h = new Array(V + 1).fill(INF);
h[V] = 0;
let all_edges = edges.slice();
for (let i = 0; i < V; i++) {
all_edges.push([V, i, 0]);
}
for (let i = 0; i < V; i++) {
for (let e of all_edges) {
if (h[e[0]]!== INF && h[e[0]] + e[2] < h[e[1]]) {
h[e[1]] = h[e[0]] + e[2];
}
}
}
hasCycle[0] = false;
for (let e of all_edges) {
if (h[e[0]]!== INF && h[e[0]] + e[2] < h[e[1]]) {
hasCycle[0] = true;
return [];
}
}
let result = h.slice(0, -1);
return result;
}
function JohnsonAlgorithm(V, edgeList) {
let hasCycle = [false];
let h = BellmanFord(V, edgeList, hasCycle);
if (hasCycle[0]) {
console.log('The graph contains a negative weight cycle. Algorithm cannot proceed.');
return;
}
let adj = new Array(V).fill(0).map(() => []);
for (let e of edgeList) {
let u = e[0], v = e[1], w = e[2];
adj[u].push(new Edge(v, w + h[u] - h[v]));
}
for (let s = 0; s < V; s++) {
let d_prime = Dijkstra(V, adj, s);
console.log('\nShortest distances from vertex', s + 1, ':');
for (let v = 0; v < V; v++) {
if (d_prime[v] === INF) {
console.log('To', v + 1, ': INF');
} else {
let actual_dist = d_prime[v] + h[v] - h[s];
console.log('To', v + 1, ':', actual_dist);
}
}
}
}
function main() {
let V = 4;
let edgeList = [
[0, 1, -5],
[0, 2, 2],
[0, 3, 3],
[1, 2, 4],
[2, 3, 1]
];
JohnsonAlgorithm(V, edgeList);
}
main();
class Edge {
constructor(to, weight) {
this.To = to;
this.Weight = weight;
}
}
class GFG {
// Using a large value for INF that won't overflow during reweighting
static INF = Number.MAX_SAFE_INTEGER / 4;
// Dijkstra's Algorithm using JavaScript's PriorityQueue
static Dijkstra(V, adj, src) {
const dist = Array(V).fill(this.INF);
const pq = new PriorityQueue();
dist[src] = 0;
pq.enqueue({ vertex: src, distance: 0 });
while (!pq.isEmpty()) {
const { vertex: u, distance: d } = pq.dequeue();
if (d > dist[u]) continue;
for (const edge of adj[u]) {
if (dist[u] + edge.Weight < dist[edge.To]) {
dist[edge.To] = dist[u] + edge.Weight;
pq.enqueue({ vertex: edge.To, distance: dist[edge.To] });
}
}
}
return dist;
}
// Bellman-Ford to find h[] and detect negative cycles
static BellmanFord(V, edges, hasCycle) {
const h = Array(V + 1).fill(this.INF);
h[V] = 0;
const allEdges = [...edges];
for (let i = 0; i < V; i++) allEdges.push([V, i, 0]);
for (let i = 0; i < V; i++) {
for (const e of allEdges) {
if (h[e[0]]!== this.INF && h[e[0]] + e[2] < h[e[1]]) {
h[e[1]] = h[e[0]] + e[2];
}
}
}
hasCycle = false;
for (const e of allEdges) {
if (h[e[0]]!== this.INF && h[e[0]] + e[2] < h[e[1]]) {
hasCycle = true;
return null;
}
}
return h.slice(0, V);
}
static JohnsonAlgorithm(V, edgeList) {
let h = this.BellmanFord(V, edgeList, (hasCycle) => hasCycle);
if (h === null) {
console.log('The graph contains a negative weight cycle. Algorithm cannot proceed.');
return;
}
const adj = Array.from({ length: V }, () => []);
for (const e of edgeList) {
const u = e[0], v = e[1], w = e[2];
adj[u].push(new Edge(v, w + h[u] - h[v]));
}
const distanceMatrix = Array.from({ length: V }, () => Array(V).fill(this.INF));
for (let s = 0; s < V; s++) {
const dPrime = this.Dijkstra(V, adj, s);
for (let v = 0; v < V; v++) {
distanceMatrix[s][v] = dPrime[v] === this.INF ? this.INF : dPrime[v] + h[v] - h[s];
}
}
// Print the Matrix without row/column numbering
console.log('Shortest Distance Matrix:');
for (let i = 0; i < V; i++) {
let row = [];
for (let j = 0; j < V; j++) {
row.push(distanceMatrix[i][j] >= this.INF / 2? 'INF' : distanceMatrix[i][j]);
}
console.log(row.join(' ').padStart(8));
}
}
}
class PriorityQueue {
constructor() {
this.elements = [];
}
enqueue(item) {
this.elements.push(item);
this.elements.sort((a, b) => a.distance - b.distance);
}
dequeue() {
return this.elements.shift();
}
isEmpty() {
return this.elements.length === 0;
}
}
// Main function
(function() {
const V = 4;
const edgeList = [
[0, 1, -5],
[0, 2, 2],
[0, 3, 3],
[1, 2, 4],
[2, 3, 1]
];
GFG.JohnsonAlgorithm(V, edgeList);
})();
Output
Shortest Distance Matrix:
0 -5 -1 0
INF 0 4 5
INF INF 0 1
INF INF INF 0
Time Complexity: The main steps in the algorithm are Bellman-Ford Algorithm called once and Dijkstra called V times. Time complexity of Bellman Ford is O(VE) and time complexity of Dijkstra is O((V + E)Log V). So overall time complexity is O(V2log V + VE).Â
The time complexity of Johnson's algorithm becomes the same as Floyd Warshall's Algorithm when the graph is complete (For a complete graph E = O(V2). But for sparse graphs, the algorithm performs much better than Floyd Warshall's Algorithm.
Auxiliary Space: O(V2)