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.
This post focusses on the implementation of Johnson’s Algorithm.
Algorithm:
- Let the given graph be G. Add a new vertex s to the graph, add edges from new vertex to all vertices of G. Let the modified graph be G’.
- Run Bellman-Ford algorithm on G’ with s as 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 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.
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 source.
Implementation:
#include<bits/stdc++.h>
using namespace std;
#define INF INT_MAX
// Function to find the vertex with minimum distance
int minDistance(vector<int> dist, vector<bool> visited) {
int minimum = INF, minVertex = 0;
for (int vertex = 0; vertex < dist.size(); vertex++) {
if (minimum > dist[vertex] && visited[vertex] == false) {
minimum = dist[vertex];
minVertex = vertex;
}
}
return minVertex;
}
// Dijkstra Algorithm for Modified Graph
void Dijkstra(vector<vector<int>> graph, vector<vector<int>> modifiedGraph, int src) {
int num_vertices = graph.size();
vector<bool> sptSet(num_vertices, false);
vector<int> dist(num_vertices, INF);
dist[src] = 0;
for (int count = 0; count < num_vertices; count++) {
int curVertex = minDistance(dist, sptSet);
sptSet[curVertex] = true;
for (int vertex = 0; vertex < num_vertices; vertex++) {
if (!sptSet[vertex] && dist[vertex] > (dist[curVertex] + modifiedGraph[curVertex][vertex]) && graph[curVertex][vertex] != 0) {
dist[vertex] = dist[curVertex] + modifiedGraph[curVertex][vertex];
}
}
}
// Print the Shortest distance from the source
for (int vertex = 0; vertex < num_vertices; vertex++) {
cout << "Vertex " << vertex << ": " << dist[vertex] << endl;
}
}
// Function to calculate shortest distances from source to all other vertices using Bellman-Ford algorithm
vector<int> BellmanFord(vector<tuple<int, int, int>> edges, vector<vector<int>> graph, int num_vertices) {
vector<int> dist(num_vertices + 1, INF);
dist[num_vertices] = 0;
for (int i = 0; i < num_vertices; i++) {
edges.push_back(make_tuple(num_vertices, i, 0));
}
for (int i = 0; i < num_vertices; i++) {
for (auto edge : edges) {
int src, des, weight;
tie(src, des, weight) = edge;
if (dist[src] != INF && dist[src] + weight < dist[des]) {
dist[des] = dist[src] + weight;
}
}
}
// Don't send the value for the source added
return vector<int>(dist.begin(), dist.begin() + num_vertices);
}
// Function to implement Johnson Algorithm
void JohnsonAlgorithm(vector<vector<int>> graph) {
vector<tuple<int, int, int>> edges;
// Create a list of edges for Bellman-Ford Algorithm
for (int i = 0; i < graph.size(); i++) {
for (int j = 0; j < graph[i].size(); j++) {
if (graph[i][j] != 0) {
edges.push_back(make_tuple(i, j, graph[i][j]));
}
}
}
// Weights used to modify the original weights
vector<int> modifyWeights = BellmanFord(edges, graph, graph.size());
vector<vector<int>> modifiedGraph(graph.size(), vector<int>(graph.size(), 0));
// Modify the weights to get rid of negative weights
for (int i = 0; i < graph.size(); i++) {
for (int j = 0; j < graph[i].size(); j++) {
if (graph[i][j] != 0) {
modifiedGraph[i][j] = graph[i][j] + modifyWeights[i] - modifyWeights[j];
}
}
}
cout << "Modified Graph: ";
for (auto row : modifiedGraph) {
for (auto val : row) {
cout << val << " ";
}
cout << endl;
}
// Run Dijkstra for every vertex as source one by one
for (int src = 0; src < graph.size(); src++) {
cout << "\nShortest Distance with vertex " << src << " as the source:\n";
Dijkstra(graph, modifiedGraph, src);
}
}
// Driver Code
int main() {
vector<vector<int>> graph = {{0, -5, 2, 3}, {0, 0, 4, 0}, {0, 0, 0, 1}, {0, 0, 0, 0}};
JohnsonAlgorithm(graph);
return 0;
}
import java.util.*;
class JohnsonAlgorithm {
static final int INF = Integer.MAX_VALUE;
// Function to calculate shortest distances from source to all other vertices using Bellman-Ford algorithm
static int[] BellmanFord(ArrayList<int[]> edges, int[][] graph, int num_vertices) {
int[] dist = new int[num_vertices + 1];
Arrays.fill(dist, INF);
dist[num_vertices] = 0;
for (int i = 0; i < num_vertices; i++) {
edges.add(new int[]{num_vertices, i, 0});
}
for (int i = 0; i < num_vertices; i++) {
for (int[] edge : edges) {
int src = edge[0], des = edge[1], weight = edge[2];
if (dist[src] != INF && dist[src] + weight < dist[des]) {
dist[des] = dist[src] + weight;
}
}
}
// Don't send the value for the source added
return Arrays.copyOfRange(dist, 0, num_vertices);
}
// Function to implement Johnson Algorithm
static void JohnsonAlgorithm(int[][] graph) {
ArrayList<int[]> edges = new ArrayList<>();
// Create a list of edges for Bellman-Ford Algorithm
for (int i = 0; i < graph.length; i++) {
for (int j = 0; j < graph[i].length; j++) {
if (graph[i][j] != 0) {
edges.add(new int[]{i, j, graph[i][j]});
}
}
}
// Weights used to modify the original weights
int[] modifyWeights = BellmanFord(edges, graph, graph.length);
int[][] modifiedGraph = new int[graph.length][graph.length];
// Modify the weights to get rid of negative weights
for (int i = 0; i < graph.length; i++) {
for (int j = 0; j < graph[i].length; j++) {
if (graph[i][j] != 0) {
modifiedGraph[i][j] = graph[i][j] + modifyWeights[i] - modifyWeights[j];
}
}
}
System.out.println("Modified Graph: " + Arrays.deepToString(modifiedGraph));
// Run Dijkstra for every vertex as source one by one
for (int src = 0; src < graph.length; src++) {
System.out.println("\nShortest Distance with vertex " + src + " as the source:\n");
Dijkstra(graph, modifiedGraph, src);
}
}
// Returns the vertex with minimum distance from the source
static int minDistance(int[] dist, boolean[] visited) {
int minimum = INF, minVertex = -1;
for (int vertex = 0; vertex < dist.length; vertex++) {
if (!visited[vertex] && dist[vertex] <= minimum) {
minimum = dist[vertex];
minVertex = vertex;
}
}
return minVertex;
}
// Dijkstra Algorithm for Modified Graph (removing negative weights)
static void Dijkstra(int[][] graph, int[][] modifiedGraph, int src) {
int num_vertices = graph.length;
int[] dist = new int[num_vertices];
Arrays.fill(dist, INF);
dist[src] = 0;
boolean[] sptSet = new boolean[num_vertices];
for (int count = 0; count < num_vertices; count++) {
int curVertex = minDistance(dist, sptSet);
sptSet[curVertex] = true;
for (int vertex = 0; vertex < num_vertices; vertex++) {
if (!sptSet[vertex] && graph[curVertex][vertex] != 0 &&
dist[curVertex] != INF && dist[curVertex] + modifiedGraph[curVertex][vertex] < dist[vertex]) {
dist[vertex] = dist[curVertex] + modifiedGraph[curVertex][vertex];
}
}
}
// Print the Shortest distance from the source
for (int vertex = 0; vertex < num_vertices; vertex++) {
System.out.println("Vertex " + vertex + ": " + dist[vertex]);
}
}
// Driver Code
public static void main(String[] args) {
int[][] graph = {
{0, -5, 2, 3},
{0, 0, 4, 0},
{0, 0, 0, 1},
{0, 0, 0, 0}
};
JohnsonAlgorithm(graph);
}
}
# Implementation of Johnson's algorithm in Python3
# Import function to initialize the dictionary
from collections import defaultdict
MAX_INT = float('Inf')
# Returns the vertex with minimum
# distance from the source
def minDistance(dist, visited):
(minimum, minVertex) = (MAX_INT, 0)
for vertex in range(len(dist)):
if minimum > dist[vertex] and visited[vertex] == False:
(minimum, minVertex) = (dist[vertex], vertex)
return minVertex
# Dijkstra Algorithm for Modified
# Graph (removing negative weights)
def Dijkstra(graph, modifiedGraph, src):
# Number of vertices in the graph
num_vertices = len(graph)
# Dictionary to check if given vertex is
# already included in the shortest path tree
sptSet = defaultdict(lambda : False)
# Shortest distance of all vertices from the source
dist = [MAX_INT] * num_vertices
dist[src] = 0
for count in range(num_vertices):
# The current vertex which is at min Distance
# from the source and not yet included in the
# shortest path tree
curVertex = minDistance(dist, sptSet)
sptSet[curVertex] = True
for vertex in range(num_vertices):
if ((sptSet[vertex] == False) and
(dist[vertex] > (dist[curVertex] +
modifiedGraph[curVertex][vertex])) and
(graph[curVertex][vertex] != 0)):
dist[vertex] = (dist[curVertex] +
modifiedGraph[curVertex][vertex]);
# Print the Shortest distance from the source
for vertex in range(num_vertices):
print ('Vertex ' + str(vertex) + ': ' + str(dist[vertex]))
# Function to calculate shortest distances from source
# to all other vertices using Bellman-Ford algorithm
def BellmanFord(edges, graph, num_vertices):
# Add a source s and calculate its min
# distance from every other node
dist = [MAX_INT] * (num_vertices + 1)
dist[num_vertices] = 0
for i in range(num_vertices):
edges.append([num_vertices, i, 0])
for i in range(num_vertices):
for (src, des, weight) in edges:
if((dist[src] != MAX_INT) and
(dist[src] + weight < dist[des])):
dist[des] = dist[src] + weight
# Don't send the value for the source added
return dist[0:num_vertices]
# Function to implement Johnson Algorithm
def JohnsonAlgorithm(graph):
edges = []
# Create a list of edges for Bellman-Ford Algorithm
for i in range(len(graph)):
for j in range(len(graph[i])):
if graph[i][j] != 0:
edges.append([i, j, graph[i][j]])
# Weights used to modify the original weights
modifyWeights = BellmanFord(edges, graph, len(graph))
modifiedGraph = [[0 for x in range(len(graph))] for y in
range(len(graph))]
# Modify the weights to get rid of negative weights
for i in range(len(graph)):
for j in range(len(graph[i])):
if graph[i][j] != 0:
modifiedGraph[i][j] = (graph[i][j] +
modifyWeights[i] - modifyWeights[j]);
print ('Modified Graph: ' + str(modifiedGraph))
# Run Dijkstra for every vertex as source one by one
for src in range(len(graph)):
print ('\nShortest Distance with vertex ' +
str(src) + ' as the source:\n')
Dijkstra(graph, modifiedGraph, src)
# Driver Code
graph = [[0, -5, 2, 3],
[0, 0, 4, 0],
[0, 0, 0, 1],
[0, 0, 0, 0]]
JohnsonAlgorithm(graph)
// Initialize the dictionary
const MAX_INT = Number.POSITIVE_INFINITY;
// Returns the vertex with minimum distance from the source
function minDistance(dist, visited) {
let minimum = MAX_INT;
let minVertex = 0;
for (let vertex = 0; vertex < dist.length; vertex++) {
if (minimum > dist[vertex] && !visited[vertex]) {
minimum = dist[vertex];
minVertex = vertex;
}
}
return minVertex;
}
// Dijkstra Algorithm for Modified Graph (removing negative weights)
function Dijkstra(graph, modifiedGraph, src) {
const numVertices = graph.length;
// Dictionary to check if given vertex
// is already included in the shortest path tree
const sptSet = new Array(numVertices).fill(false);
// Shortest distance of all vertices from the source
const dist = new Array(numVertices).fill(MAX_INT);
dist[src] = 0;
for (let count = 0; count < numVertices; count++) {
// The current vertex which is at min Distance
// from the source and not yet included in the shortest path tree
const curVertex = minDistance(dist, sptSet);
sptSet[curVertex] = true;
for (let vertex = 0; vertex < numVertices; vertex++) {
if (
!sptSet[vertex] &&
dist[vertex] > dist[curVertex] + modifiedGraph[curVertex][vertex] &&
graph[curVertex][vertex] !== 0
) {
dist[vertex] = dist[curVertex] + modifiedGraph[curVertex][vertex];
}
}
}
// Print the Shortest distance from the source
for (let vertex = 0; vertex < numVertices; vertex++) {
console.log(`Vertex ${vertex}: ${dist[vertex]}`);
}
}
// Function to calculate shortest distances from source to all other vertices using Bellman-Ford algorithm
function BellmanFord(edges, graph, numVertices) {
// Add a source s and calculate its min distance from every other node
const dist = new Array(numVertices + 1).fill(MAX_INT);
dist[numVertices] = 0;
for (let i = 0; i < numVertices; i++) {
edges.push([numVertices, i, 0]);
}
for (let i = 0; i < numVertices; i++) {
for (const [src, des, weight] of edges) {
if (dist[src] !== MAX_INT && dist[src] + weight < dist[des]) {
dist[des] = dist[src] + weight;
}
}
}
// Don't send the value for the source added
return dist.slice(0, numVertices);
}
// Function to implement Johnson Algorithm
function JohnsonAlgorithm(graph) {
const edges = [];
// Create a list of edges for Bellman-Ford Algorithm
for (let i = 0; i < graph.length; i++) {
for (let j = 0; j < graph[i].length; j++) {
if (graph[i][j] !== 0) {
edges.push([i, j, graph[i][j]]);
}
}
}
// Weights used to modify the original weights
const modifyWeights = BellmanFord(edges, graph, graph.length);
const modifiedGraph = Array(graph.length).fill().map(() => Array(graph.length).fill(0));
// Modify the weights to get rid of negative weights
for (let i = 0; i < graph.length; i++) {
for (let j = 0; j < graph[i].length; j++) {
if (graph[i][j] !== 0) {
modifiedGraph[i][j] = graph[i][j] + modifyWeights[i] - modifyWeights[j];
}
}
}
console.log("Modified Graph: " + JSON.stringify(modifiedGraph)+"<br>");
// Run Dijkstra for every vertex as source one by one
for (let src = 0; src < graph.length; src++) {
console.log("<br>"+ "Shortest Distance with vertex " + src + " as the source:"+"<br>");
Dijkstra(graph, modifiedGraph, src);
}
}
// Driver Code
const graph = [[0, -5, 2, 3],
[0, 0, 4, 0],
[0, 0, 0, 1],
[0, 0, 0, 0]
];
JohnsonAlgorithm(graph);
Output
Modified Graph: [[0, 0, 3, 3], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]] Shortest Distance with vertex 0 as the source: Vertex 0: 0 Vertex 1: 0 Vertex 2: 0 Vertex 3: 0 Shortest Distance with vertex 1 as the source: Vertex 0: inf Vertex 1: 0 Vertex 2: 0 Vertex 3: 0 Shortest Distance with vertex 2 as the source: Vertex 0: inf Vertex 1: inf Vertex 2: 0 Vertex 3: 0 Shortest Distance with vertex 3 as the source: Vertex 0: inf Vertex 1: inf Vertex 2: inf Vertex 3: 0
Time Complexity: The time complexity of the above algorithm is [Tex]O(V^3 + V*E) [/Tex]as Dijkstra’s Algorithm takes [Tex]O(n^2) [/Tex]for adjacency matrix. Note that the above algorithm can be made more efficient by using adjacency list instead of the adjacency matrix to represent the Graph.
Contact Us