Implementation of Johnson’s algorithm for all-pairs shortest paths
Last Updated :
15 May, 2024
Johnson’s algorithm finds the shortest paths between all pairs of vertices in a weighted directed graph. It allows some of the edge weights to be negative numbers, but no negative-weight cycles may exist. It uses the Bellman-Ford algorithm to re-weight the original graph, removing all negative weights. Dijkstra’s algorithm is applied on the re-weighted graph to compute the shortest path between all pairs of vertices.
Algorithm Description
Using Dijkstra’s algorithm, the shortest paths between all pairs of vertices in O(V2logV) can be found. However, Dijkstra does not work with negative weights. To avoid this problem, Johnson’s algorithm uses a technique called reweighting.
Reweighting is a process by which each edge weight is changed to satisfy two properties-
- For all pairs of vertices u, v in the graph, if the shortest path exists between those vertices before reweighting, it must also be the shortest path between those vertices after reweighting.
- For all edges, (u, v), in the graph, they must have a non-negative weight (u, v).
Johnson’s algorithm uses Bellman-Ford to reweight the edges. Bellman-Ford is also able to detect negative weight cycles if present in the original graph.
Graph Representation
Adjacency List is modified a bit to represent the graph. For each source vertex s, each of its neighboring vertices has two properties associated with them:
- Destination
- Weight
Consider the graph –

Source vertex 0 has one neighboring vertex, one whose destination is 2 and weight is -2. Each neighboring vertex is encapsulated using a static Neighbor class.
C++
class Neighbour {
public:
int destination;
int weight;
Neighbour(int destination, int weight)
{
this->destination = destination;
this->weight = weight;
}
};
//This code is contributed by Akash Jha
Java
private static class Neighbour {
int destination;
int weight;
Neighbour(int destination, int weight)
{
this.destination = destination;
this.weight = weight;
}
}
Python
class Neighbour:
def __init__(self, destination, weight):
self.destination = destination
self.weight = weight
#This code is contributed by Akash Jha
C#
private class Neighbour {
public int destination;
public int weight;
public Neighbour(int destination, int weight)
{
this.destination = destination;
this.weight = weight;
}
}
//This code is contributed by Akash Jha
JavaScript
class Neighbour {
constructor(destination, weight) {
this.destination = destination;
this.weight = weight;
}
}
//This code is contributed by Akash Jha
Pseudocode
Follow the steps below to solve the problem:
- Add a new node q to the graph, connected by zero-weight edges to all the other nodes.
- Use the Bellman-Ford algorithm, starting from the new vertex q, to find for each vertex v the minimum weight h(v) of a path from q to v. If this step detects a negative cycle, the algorithm is terminated.
- Reweight the edges of the original graph using the values computed by the Bellman-Ford algorithm: an edge from u to v, having length w(u, v) reweighted to w(u, v) + h(u) − h(v).
- Remove q and apply Dijkstra’s algorithm to find the shortest paths from each node s to every other vertex in the reweighted graph.
- Compute the distance in the original graph by adding h(v) − h(u) to the distance returned by Dijkstra’s algorithm.
Below is the implementation of the above approach:
C++
#include <bits/stdc++.h>
using namespace std;
// Structure to represent a graph
struct Graph {
int V;
vector<vector<pair<int, int> > > adj;
// Constructor
Graph(int V)
{
this->V = V;
adj.resize(V);
}
// Add edge to the graph
void addEdge(int u, int v, int w)
{
adj[u].push_back({ v, w });
}
// Implementing Dijkstra Algorithm
vector<int> dijkstra(int src)
{
vector<int> dist(V, INT_MAX);
dist[src] = 0;
priority_queue<pair<int, int>,
vector<pair<int, int> >,
greater<pair<int, int> > >
pq;
pq.push({ 0, src });
while (!pq.empty()) {
int u = pq.top().second;
pq.pop();
for (auto x : adj[u]) {
int v = x.first;
int weight = x.second;
if (dist[v] > dist[u] + weight) {
dist[v] = dist[u] + weight;
pq.push({ dist[v], v });
}
}
}
return dist;
}
// Implementing Bellman Ford Algorithm
vector<int> bellmanFord(int src)
{
vector<int> dist(V, INT_MAX);
dist[src] = 0;
for (int i = 1; i <= V - 1; i++) {
for (int u = 0; u < V; u++) {
for (auto x : adj[u]) {
int v = x.first;
int weight = x.second;
if (dist[u] != INT_MAX
&& dist[u] + weight < dist[v])
dist[v] = dist[u] + weight;
}
}
}
for (int u = 0; u < V; u++) {
for (auto x : adj[u]) {
int v = x.first;
int weight = x.second;
if (dist[u] != INT_MAX
&& dist[u] + weight < dist[v]) {
cout << "Graph contains negative "
"weight cycle"
<< endl;
return {};
}
}
}
return dist;
}
// Implementing Johnson Algorithm
vector<vector<int> > johnsons()
{
// Add a new vertex connected by zero-weight edges
// to all other vertices
V++;
adj.resize(V);
for (int i = 0; i < V - 1; i++)
adj[V - 1].push_back({ i, 0 });
// Use Bellman Ford to compute a set of weights for
// reweighting the edges
vector<int> h = bellmanFord(V - 1);
if (h.empty())
return {};
// Reweight the edges
for (int u = 0; u < V; u++) {
for (auto& x : adj[u]) {
int v = x.first;
int& weight = x.second;
weight = weight + h[u] - h[v];
}
}
// Remove the added vertex and apply Dijkstra from
// each vertex to all others in the reweighted graph
V--;
adj.pop_back();
vector<vector<int> > distances(V, vector<int>(V));
for (int s = 0; s < V; s++)
distances[s] = dijkstra(s);
// Compute the distances in the original graph by
// adjusting the distances returned by Dijkstra's
// algorithm
for (int u = 0; u < V; u++) {
for (int v = 0; v < V; v++) {
if (distances[u][v] == INT_MAX)
continue;
distances[u][v] += (h[v] - h[u]);
}
}
return distances;
}
};
// Driver code
int main()
{
int V = 4;
vector<vector<int> > matrix = { { 0, 0, -2, 0 },
{ 4, 0, 3, 0 },
{ 0, 0, 0, 2 },
{ 0, -1, 0, 0 } };
// Initialization
Graph g(V);
for (int i = 0; i < V; i++) {
for (int j = 0; j < V; j++) {
if (matrix[i][j] != 0)
g.addEdge(i, j, matrix[i][j]);
}
}
// Function Call
vector<vector<int> > distances = g.johnsons();
if (distances.empty()) {
cout << "Negative weight cycle detected." << endl;
return 0;
}
// Print the distance matrix
cout << "Distance matrix:" << endl;
cout << " \t";
for (int i = 0; i < V; i++)
cout << setw(3) << i << "\t";
for (int i = 0; i < V; i++) {
cout << endl << setw(3) << i << "\t";
for (int j = 0; j < V; j++) {
if (distances[i][j] == INT_MAX)
cout << " X\t";
else
cout << setw(3) << distances[i][j] << "\t";
}
}
return 0;
}
Java
// Java program for the above approach
import java.util.ArrayList;
import java.util.Arrays;
public class Graph {
private static class Neighbour {
int destination;
int weight;
Neighbour(int destination, int weight)
{
this.destination = destination;
this.weight = weight;
}
}
private int vertices;
private final ArrayList<ArrayList<Neighbour> >
adjacencyList;
// On using the below constructor,
// edges must be added manually
// to the graph using addEdge()
public Graph(int vertices)
{
this.vertices = vertices;
adjacencyList = new ArrayList<>(vertices);
for (int i = 0; i < vertices; i++)
adjacencyList.add(new ArrayList<>());
}
// On using the below constructor,
// edges will be added automatically
// to the graph using the adjacency matrix
public Graph(int vertices, int[][] adjacencyMatrix)
{
this(vertices);
for (int i = 0; i < vertices; i++) {
for (int j = 0; j < vertices; j++) {
if (adjacencyMatrix[i][j] != 0)
addEdge(i, j, adjacencyMatrix[i][j]);
}
}
}
public void addEdge(int source, int destination,
int weight)
{
adjacencyList.get(source).add(
new Neighbour(destination, weight));
}
// Time complexity of this
// implementation of dijkstra is O(V^2).
public int[] dijkstra(int source)
{
boolean[] isVisited = new boolean[vertices];
int[] distance = new int[vertices];
Arrays.fill(distance, Integer.MAX_VALUE);
distance[source] = 0;
for (int vertex = 0; vertex < vertices; vertex++) {
int minDistanceVertex = findMinDistanceVertex(
distance, isVisited);
isVisited[minDistanceVertex] = true;
for (Neighbour neighbour :
adjacencyList.get(minDistanceVertex)) {
int destination = neighbour.destination;
int weight = neighbour.weight;
if (!isVisited[destination]
&& distance[minDistanceVertex] + weight
< distance[destination])
distance[destination]
= distance[minDistanceVertex]
+ weight;
}
}
return distance;
}
// Method used by `int[] dijkstra(int)`
private int findMinDistanceVertex(int[] distance,
boolean[] isVisited)
{
int minIndex = -1, minDistance = Integer.MAX_VALUE;
for (int vertex = 0; vertex < vertices; vertex++) {
if (!isVisited[vertex]
&& distance[vertex] <= minDistance) {
minDistance = distance[vertex];
minIndex = vertex;
}
}
return minIndex;
}
// Returns null if
// negative weight cycle is detected
public int[] bellmanford(int source)
{
int[] distance = new int[vertices];
Arrays.fill(distance, Integer.MAX_VALUE);
distance[source] = 0;
for (int i = 0; i < vertices - 1; i++) {
for (int currentVertex = 0;
currentVertex < vertices;
currentVertex++) {
for (Neighbour neighbour :
adjacencyList.get(currentVertex)) {
if (distance[currentVertex]
!= Integer.MAX_VALUE
&& distance[currentVertex]
+ neighbour.weight
< distance
[neighbour
.destination]) {
distance[neighbour.destination]
= distance[currentVertex]
+ neighbour.weight;
}
}
}
}
for (int currentVertex = 0;
currentVertex < vertices; currentVertex++) {
for (Neighbour neighbour :
adjacencyList.get(currentVertex)) {
if (distance[currentVertex]
!= Integer.MAX_VALUE
&& distance[currentVertex]
+ neighbour.weight
< distance[neighbour
.destination])
return null;
}
}
return distance;
}
// Returns null if negative
// weight cycle is detected
public int[][] johnsons()
{
// Add a new vertex q to the original graph,
// connected by zero-weight edges to
// all the other vertices of the graph
this.vertices++;
adjacencyList.add(new ArrayList<>());
for (int i = 0; i < vertices - 1; i++)
adjacencyList.get(vertices - 1)
.add(new Neighbour(i, 0));
// Use bellman ford with the new vertex q
// as source, to find for each vertex v
// the minimum weight h(v) of a path
// from q to v.
// If this step detects a negative cycle,
// the algorithm is terminated.
int[] h = bellmanford(vertices - 1);
if (h == null)
return null;
// Re-weight the edges of the original graph using
// the values computed by the Bellman-Ford
// algorithm. w'(u, v) = w(u, v) + h(u) - h(v).
for (int u = 0; u < vertices; u++) {
ArrayList<Neighbour> neighbours
= adjacencyList.get(u);
for (Neighbour neighbour : neighbours) {
int v = neighbour.destination;
int w = neighbour.weight;
// new weight
neighbour.weight = w + h[u] - h[v];
}
}
// Step 4: Remove edge q and apply Dijkstra
// from each node s to every other vertex
// in the re-weighted graph
adjacencyList.remove(vertices - 1);
vertices--;
int[][] distances = new int[vertices][];
for (int s = 0; s < vertices; s++)
distances[s] = dijkstra(s);
// Compute the distance in the original graph
// by adding h[v] - h[u] to the
// distance returned by dijkstra
for (int u = 0; u < vertices; u++) {
for (int v = 0; v < vertices; v++) {
// If no edge exist, continue
if (distances[u][v] == Integer.MAX_VALUE)
continue;
distances[u][v] += (h[v] - h[u]);
}
}
return distances;
}
// Driver Code
public static void main(String[] args)
{
final int vertices = 4;
final int[][] matrix = { { 0, 0, -2, 0 },
{ 4, 0, 3, 0 },
{ 0, 0, 0, 2 },
{ 0, -1, 0, 0 } };
// Initialization
Graph graph = new Graph(vertices, matrix);
// Function Call
int[][] distances = graph.johnsons();
if (distances == null) {
System.out.println(
"Negative weight cycle detected.");
return;
}
// The code fragment below outputs
// an formatted distance matrix.
// Its first row and first
// column represent vertices
System.out.println("Distance matrix:");
System.out.print(" \t");
for (int i = 0; i < vertices; i++)
System.out.printf("%3d\t", i);
for (int i = 0; i < vertices; i++) {
System.out.println();
System.out.printf("%3d\t", i);
for (int j = 0; j < vertices; j++) {
if (distances[i][j] == Integer.MAX_VALUE)
System.out.print(" X\t");
else
System.out.printf("%3d\t",
distances[i][j]);
}
}
}
}
Python
import heapq
class Graph:
class Neighbour:
def __init__(self, destination, weight):
self.destination = destination
self.weight = weight
def __init__(self, vertices, adjacency_matrix):
self.vertices = vertices
self.adjacency_list = [[] for _ in range(vertices)]
for i in range(vertices):
for j in range(vertices):
if adjacency_matrix[i][j] != 0:
self.add_edge(i, j, adjacency_matrix[i][j])
def add_edge(self, source, destination, weight):
self.adjacency_list[source].append(self.Neighbour(destination, weight))
def dijkstra(self, source):
is_visited = [False] * self.vertices
distance = [float('inf')] * self.vertices
distance[source] = 0
heap = [(0, source)]
while heap:
dist, node = heapq.heappop(heap)
if is_visited[node]:
continue
is_visited[node] = True
for neighbour in self.adjacency_list[node]:
if (not is_visited[neighbour.destination] and
dist + neighbour.weight < distance[neighbour.destination]):
distance[neighbour.destination] = dist + neighbour.weight
heapq.heappush(
heap, (distance[neighbour.destination], neighbour.destination))
return distance
def bellmanford(self, source):
distance = [float('inf')] * self.vertices
distance[source] = 0
for _ in range(self.vertices - 1):
for current_vertex in range(self.vertices):
for neighbour in self.adjacency_list[current_vertex]:
if (distance[current_vertex] != float('inf') and
distance[current_vertex] + neighbour.weight < distance[neighbour.destination]):
distance[neighbour.destination] = distance[current_vertex] + \
neighbour.weight
for current_vertex in range(self.vertices):
for neighbour in self.adjacency_list[current_vertex]:
if (distance[current_vertex] != float('inf') and
distance[current_vertex] + neighbour.weight < distance[neighbour.destination]):
return None
return distance
def johnsons(self):
self.vertices += 1
self.adjacency_list.append([self.Neighbour(i, 0)
for i in range(self.vertices - 1)])
h = self.bellmanford(self.vertices - 1)
if h is None:
return None
for u in range(self.vertices):
for neighbour in self.adjacency_list[u]:
v, w = neighbour.destination, neighbour.weight
neighbour.weight = w + h[u] - h[v]
self.adjacency_list.pop()
self.vertices -= 1
distances = []
for s in range(self.vertices):
distances.append(self.dijkstra(s))
for u in range(self.vertices):
for v in range(self.vertices):
if distances[u][v] != float('inf'):
distances[u][v] += h[v] - h[u]
return distances
# Driver Code
if __name__ == "__main__":
vertices = 4
matrix = [[0, 0, -2, 0],
[4, 0, 3, 0],
[0, 0, 0, 2],
[0, -1, 0, 0]]
# Initialization
graph = Graph(vertices, matrix)
# Function Call
distances = graph.johnsons()
if distances is None:
print("Negative weight cycle detected.")
else:
print("Distance matrix:")
print("\t", end="")
for i in range(vertices):
print(f"{i}\t", end="")
print()
for i in range(vertices):
print(f"{i}\t", end="")
for j in range(vertices):
if distances[i][j] == float('inf'):
print(" X\t", end="")
else:
print(f"{distances[i][j]}\t", end="")
print()
JavaScript
class Graph {
constructor(vertices) {
this.vertices = vertices;
this.adjacencyList = new Array(vertices).fill().map(() => []);
}
addEdge(source, destination, weight) {
this.adjacencyList[source].push({ destination, weight });
}
dijkstra(source) {
const isVisited = new Array(this.vertices).fill(false);
const distance = new Array(this.vertices).fill(Infinity);
distance[source] = 0;
for (let i = 0; i < this.vertices; i++) {
const minDistanceVertex = this.findMinDistanceVertex(distance, isVisited);
isVisited[minDistanceVertex] = true;
this.adjacencyList[minDistanceVertex].forEach(neighbour => {
const { destination, weight } = neighbour;
if (!isVisited[destination] && distance[minDistanceVertex] + weight < distance[destination]) {
distance[destination] = distance[minDistanceVertex] + weight;
}
});
}
return distance;
}
findMinDistanceVertex(distance, isVisited) {
let minIndex = -1,
minDistance = Infinity;
for (let vertex = 0; vertex < this.vertices; vertex++) {
if (!isVisited[vertex] && distance[vertex] <= minDistance) {
minDistance = distance[vertex];
minIndex = vertex;
}
}
return minIndex;
}
bellmanFord(source) {
const distance = new Array(this.vertices).fill(Infinity);
distance[source] = 0;
for (let i = 0; i < this.vertices - 1; i++) {
for (let currentVertex = 0; currentVertex < this.vertices; currentVertex++) {
this.adjacencyList[currentVertex].forEach(neighbour => {
if (distance[currentVertex] !== Infinity && distance[currentVertex] + neighbour.weight < distance[neighbour.destination]) {
distance[neighbour.destination] = distance[currentVertex] + neighbour.weight;
}
});
}
}
for (let currentVertex = 0; currentVertex < this.vertices; currentVertex++) {
this.adjacencyList[currentVertex].forEach(neighbour => {
if (distance[currentVertex] !== Infinity && distance[currentVertex] + neighbour.weight < distance[neighbour.destination]) {
return null;
}
});
}
return distance;
}
johnsons() {
this.vertices++;
this.adjacencyList.push(Array.from({ length: this.vertices - 1 }, (_, i) => ({ destination: i, weight: 0 })));
const h = this.bellmanFord(this.vertices - 1);
if (h === null) return null;
for (let u = 0; u < this.vertices; u++) {
this.adjacencyList[u].forEach(neighbour => {
const v = neighbour.destination;
const w = neighbour.weight;
neighbour.weight = w + h[u] - h[v];
});
}
this.vertices--;
this.adjacencyList.pop();
const distances = new Array(this.vertices).fill().map(() => []);
for (let s = 0; s < this.vertices; s++) {
distances[s] = this.dijkstra(s);
}
for (let u = 0; u < this.vertices; u++) {
for (let v = 0; v < this.vertices; v++) {
if (distances[u][v] === Infinity) continue;
distances[u][v] += (h[v] - h[u]);
}
}
return distances;
}
}
const vertices = 4;
const matrix = [
[0, 0, -2, 0],
[4, 0, 3, 0],
[0, 0, 0, 2],
[0, -1, 0, 0]
];
const graph = new Graph(vertices);
matrix.forEach((row, source) => {
row.forEach((weight, destination) => {
if (weight !== 0) graph.addEdge(source, destination, weight);
});
});
const distances = graph.johnsons();
if (distances === null) {
console.log("Negative weight cycle detected.");
} else {
console.log("Distance matrix:");
console.log(" \t" + Array.from({ length: vertices }, (_, i) => i).join("\t"));
for (let i = 0; i < vertices; i++) {
process.stdout.write(`${i}\t`);
for (let j = 0; j < vertices; j++) {
if (distances[i][j] === Infinity) {
process.stdout.write("X\t");
} else {
process.stdout.write(`${distances[i][j]}\t`);
}
}
console.log();
}
}
OutputDistance matrix:
0 1 2 3
0 0 -1 -2 0
1 4 0 2 4
2 5 1 0 2
3 3 -1 1 0
Time Complexity: O(V2log V + VE), The time complexity of Johnson’s algorithm becomes the same as Floyd Warshall when the graphs are complete (For a complete graph E = O(V2). But for sparse graphs, the algorithm performs much better than Floyd Warshall.
Auxiliary Space: O(V*V)
Similar Reads
Johnsonâs algorithm for All-pairs shortest paths | Implementation
Given a weighted Directed Graph where the weights may be negative, find the shortest path between every pair of vertices in the Graph using Johnson's Algorithm. The detailed explanation of Johnson's algorithm has already been discussed in the previous post. Refer Johnsonâs algorithm for All-pairs
12 min read
Johnson's algorithm for All-pairs shortest paths
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 a
15+ min read
Applications of Dijkstra's shortest path algorithm
Dijkstraâs algorithm is one of the most popular algorithms for solving many single-source shortest path problems having non-negative edge weight in the graphs i.e., it is to find the shortest distance between two vertices on a graph. It was conceived by computer scientist Edsger W. Dijkstra in 1956
4 min read
Bellman Ford Algorithm (Simple Implementation)
We have introduced Bellman Ford and discussed on implementation here.Input: Graph and a source vertex src Output: Shortest distance to all vertices from src. If there is a negative weight cycle, then shortest distances are not calculated, negative weight cycle is reported.1) This step initializes di
13 min read
What is Dijkstraâs Algorithm? | Introduction to Dijkstra's Shortest Path Algorithm
In this article, we will be discussing one of the most commonly known shortest-path algorithms i.e. Dijkstra's Shortest Path Algorithm which was developed by Dutch computer scientist Edsger W. Dijkstra in 1956. Moreover, we will do a complexity analysis for this algorithm and also see how it differs
10 min read
Comparison between Shortest Path Algorithms:
Finding the shortest way is becoming more important in a world where time and distance matter. There are multiple shorted path algorithms exists. Therefore, in this article, we will compare the shortest path algorithms on the basis of their complexity and their performance, which will help us to use
4 min read
HopcroftâKarp Algorithm for Maximum Matching | Set 2 (Implementation)
We strongly recommend to refer below post as a prerequisite.HopcroftâKarp Algorithm for Maximum Matching | Set 1 (Introduction) There are few important things to note before we start implementation. We need to find an augmenting path (A path that alternates between matching and not matching edges an
13 min read
Introduction and implementation of Karger's algorithm for Minimum Cut
Given an undirected and unweighted graph, find the smallest cut (smallest number of edges that disconnects the graph into two components). The input graph may have parallel edges. For example consider the following example, the smallest cut has 2 edges. A Simple Solution use Max-Flow based s-t cut a
15+ min read
Printing Paths in Dijkstra's Shortest Path Algorithm
Given a graph and a source vertex in the graph, find the shortest paths from the source to all vertices in the given graph.We have discussed Dijkstra's Shortest Path algorithm in the below posts. Dijkstraâs shortest path for adjacency matrix representationDijkstraâs shortest path for adjacency list
15 min read
Sum of all pair shortest paths in a Tree
Given a weighted undirected graph T consisting of nodes valued [0, N - 1] and an array Edges[][3] of type {u, v, w} that denotes an edge between vertices u and v having weight w. The task is to find the sum of all pair shortest paths in the given tree. Examples: Input: N = 3, Edges[][] = {{0, 2, 15}
15+ min read