Skip to main content

Johnson's Algorithm

Johnson's Algorithm is a versatile method to find the shortest paths between all pairs of nodes in a weighted graph, including graphs with both positive and negative weights (but no negative cycles). It combines Bellman-Ford and Dijkstra’s algorithms to achieve an efficient solution.

Key Features:​

  • Time Complexity: O(V² log V + VE) where V is the number of vertices, and E is the number of edges.
  • Space Complexity: O(V²) for storing distances between every pair.
  • Allows for graphs with negative edge weights as long as there are no negative weight cycles.

Applications:​

  • Optimal pathfinding in transportation networks with varying costs.
  • Network routing for latency optimization.
  • Economic or logistic networks with mixed costs.

Code in C

#include <stdio.h>
#include <stdlib.h>
#include <limits.h>

#define V 5

struct Edge {
int src, dest, weight;
};

struct Graph {
int V, E;
struct Edge* edge;
};

struct Graph* createGraph(int V, int E) {
struct Graph* graph = (struct Graph*)malloc(sizeof(struct Graph));
graph->V = V;
graph->E = E;
graph->edge = (struct Edge*)malloc(E * sizeof(struct Edge));
return graph;
}

void bellmanFord(struct Graph* graph, int src, int dist[]) {
for (int i = 0; i < graph->V; i++)
dist[i] = INT_MAX;
dist[src] = 0;

for (int i = 1; i <= graph->V - 1; i++) {
for (int j = 0; j < graph->E; j++) {
int u = graph->edge[j].src;
int v = graph->edge[j].dest;
int weight = graph->edge[j].weight;
if (dist[u] != INT_MAX && dist[u] + weight < dist[v])
dist[v] = dist[u] + weight;
}
}
}

void dijkstra(int graph[V][V], int src, int dist[]) {
int sptSet[V] = {0};
for (int i = 0; i < V; i++)
dist[i] = INT_MAX;
dist[src] = 0;

for (int count = 0; count < V - 1; count++) {
int u = -1;
int min = INT_MAX;
for (int v = 0; v < V; v++)
if (!sptSet[v] && dist[v] <= min)
min = dist[v], u = v;

sptSet[u] = 1;

for (int v = 0; v < V; v++)
if (!sptSet[v] && graph[u][v] && dist[u] != INT_MAX
&& dist[u] + graph[u][v] < dist[v])
dist[v] = dist[u] + graph[u][v];
}
}

void johnsonsAlgorithm(struct Graph* graph) {
int V = graph->V;
int newGraph[V + 1][V + 1];
int h[V + 1];

for (int i = 0; i <= V; i++)
for (int j = 0; j <= V; j++)
newGraph[i][j] = 0;

for (int i = 0; i < V; i++)
for (int j = 0; j < V; j++)
newGraph[i][j] = graph->edge[i].weight;

bellmanFord(graph, V, h);

int reweightedGraph[V][V];
for (int u = 0; u < V; u++) {
for (int v = 0; v < V; v++) {
if (graph->edge[u].weight != INT_MAX)
reweightedGraph[u][v] = graph->edge[u].weight + h[u] - h[v];
}
}

printf("All-pairs shortest paths:\n");
for (int u = 0; u < V; u++) {
int dist[V];
dijkstra(reweightedGraph, u, dist);
for (int v = 0; v < V; v++)
printf("Distance from %d to %d is %d\n", u, v, dist[v]);
}
}

int main() {
int V = 5;
int E = 10;
struct Graph* graph = createGraph(V, E);

// Define edges with source, destination, and weight
graph->edge[0].src = 0;
graph->edge[0].dest = 1;
graph->edge[0].weight = 3;

// Add remaining edges here...

johnsonsAlgorithm(graph);
return 0;
}

Code in Cpp

#include <iostream>
#include <vector>
#include <queue>
#include <limits.h>

using namespace std;

void bellmanFord(vector<vector<pair<int, int>>>& graph, int src, int V, vector<int>& h) {
h.assign(V + 1, INT_MAX);
h[src] = 0;

for (int i = 0; i < V; i++) {
for (int u = 0; u < V; u++) {
for (auto& edge : graph[u]) {
int v = edge.first;
int weight = edge.second;
if (h[u] != INT_MAX && h[u] + weight < h[v]) {
h[v] = h[u] + weight;
}
}
}
}
}

void dijkstra(vector<vector<pair<int, int>>>& graph, int src, int V, vector<int>& dist) {
dist.assign(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;
int uDist = pq.top().first;
pq.pop();

if (uDist > dist[u]) continue;

for (auto& edge : graph[u]) {
int v = edge.first;
int weight = edge.second;
if (uDist + weight < dist[v]) {
dist[v] = uDist + weight;
pq.push({dist[v], v});
}
}
}
}

void johnsonsAlgorithm(vector<vector<pair<int, int>>>& graph, int V) {
vector<vector<pair<int, int>>> newGraph = graph;
for (int i = 0; i < V; i++)
newGraph.push_back({}); // Add extra vertex with zero-weight edges
vector<int> h;
bellmanFord(newGraph, V, V, h);

vector<vector<pair<int, int>>> reweightedGraph(V);
for (int u = 0; u < V; u++) {
for (auto& edge : graph[u]) {
int v = edge.first;
int weight = edge.second + h[u] - h[v];
reweightedGraph[u].push_back({v, weight});
}
}

for (int u = 0; u < V; u++) {
vector<int> dist;
dijkstra(reweightedGraph, u, V, dist);

cout << "Distances from vertex " << u << ":\n";
for (int v = 0; v < V; v++)
cout << (dist[v] == INT_MAX ? "INF" : to_string(dist[v] + h[v] - h[u])) << " ";
cout << endl;
}
}

int main() {
int V = 5;
vector<vector<pair<int, int>>> graph(V);
graph[0] = {{1, 3}, {4, -4}};
graph[1] = {{2, 8}, {4, 7}};
graph[2] = {{3, -5}};
graph[3] = {{0, 2}};
graph[4] = {{3, 6}};

johnsonsAlgorithm(graph, V);
return 0;
}

Code in Python

import heapq

def bellman_ford(graph, V, src):
dist = [float("inf")] * V
dist[src] = 0
for _ in range(V - 1):
for u in range(V):
for v, weight in graph[u]:
if dist[u] != float("inf") and dist[u] + weight < dist[v]:
dist[v] = dist[u] + weight
return dist

def dijkstra(graph, src, V):
dist = [float("inf")] * V
dist[src] = 0
min_heap = [(0, src)]
while min_heap:
u_distance, u = heapq.heappop(min_heap)
for v, weight in graph[u]:
if u_distance + weight < dist[v]:
dist[v] = u_distance + weight
heapq.heappush(min_heap, (dist[v], v))
return dist

def johnsons_algorithm(graph, V):
new_graph = [[] for _ in range(V + 1)]
for u in range(V):
new_graph[u].extend(graph[u])
new_graph[V].append((u, 0))

h = bellman_ford(new_graph, V + 1, V)

reweighted_graph = [[] for _ in range(V)]
for u in range(V):
for v, weight in graph[u]:
reweighted_graph[u].append((v, weight + h[u] - h[v]))

distances = []
for u in range(V):
dist = dijkstra(reweighted_graph, u, V)
distances.append([d + h[v] - h[u] for v, d in enumerate(dist)])

return distances

# Example usage
V = 4
graph = [
[(1, 3), (2, 8)],
[(3, -2)],
[(1, 4)],
[]
]

distances = johnsons_algorithm(graph, V)
print("Shortest path distances between all pairs:")
for u in range(V):
print(f"From vertex {u}: {distances[u]}")

Code in Java

import java.util.*;

class JohnsonsAlgorithm {
static final int INF = Integer.MAX_VALUE;

public static void bellmanFord(List<int[]>[] graph, int src, int[] h) {
Arrays.fill(h, INF);
h[src] = 0;

int V = graph.length;
for (int i = 0; i < V - 1; i++) {
for (int u = 0; u < V; u++) {
for (int[] edge : graph[u]) {
int v = edge[0];
int weight = edge[1];
if (h[u] != INF && h[u] + weight < h[v]) {
h[v] = h[u] + weight;
}
}
}
}
}

public static void dijkstra(List<int[]>[] graph, int src, int[] dist) {
Arrays.fill(dist, INF);
dist[src] = 0;

PriorityQueue<int[]> pq = new PriorityQueue<>(Comparator.comparingInt(a -> a[1]));
pq.add(new int[]{src, 0});

while (!pq.isEmpty()) {
int[] current = pq.poll();
int u = current[0], uDist = current[1];

if (uDist > dist[u]) continue;

for (int[] edge : graph[u]) {
int v = edge[0], weight = edge[1];
if (uDist + weight < dist[v]) {
dist[v] = uDist + weight;
pq.add(new int[]{v, dist[v]});
}
}
}
}

public static void johnsonsAlgorithm(List<int[]>[] graph) {
int V = graph.length;

List<int[]>[] newGraph = new ArrayList[V + 1];
for (int i = 0; i <= V; i++) newGraph[i] = new ArrayList<>();
for (int u = 0; u < V; u++) {
for (int[] edge : graph[u]) newGraph[u].add(new int[]{edge[0], edge[1]});
newGraph[V].add(new int[]{u, 0});
}

int[] h = new int[V + 1];
bellmanFord(newGraph, V, h);

List<int[]>[] reweightedGraph = new ArrayList[V];
for (int u = 0; u < V; u++) reweightedGraph[u] = new ArrayList<>();

for (int u = 0; u < V; u++) {
for (int[] edge : graph[u]) {
int v = edge[0], weight = edge[1] + h[u] - h[v];
reweightedGraph[u].add(new int[]{v, weight});
}
}

for (int u = 0; u < V; u++) {
int[] dist = new int[V];
dijkstra(reweightedGraph, u, dist);

System.out.println("Distances from vertex " + u + ":");
for (int v = 0; v < V; v++) {
System.out.print((dist[v] == INF ? "INF" : dist[v] + h[v] - h[u]) + " ");
}
System.out.println();
}
}

public static void main(String[] args) {
int V = 5;
List<int[]>[] graph = new ArrayList[V];
for (int i = 0; i < V; i++) graph[i] = new ArrayList<>();

graph[0].add(new int[]{1, 3});
graph[0].add(new int[]{4, -4});
graph[1].add(new int[]{2, 8});
graph[1].add(new int[]{4, 7});
graph[2].add(new int[]{3, -5});
graph[3].add(new int[]{0, 2});
graph[4].add(new int[]{3, 6});

johnsonsAlgorithm(graph);
}
}

Example:​

Input:​

  • Graph vertices: 4
  • Edges:
  - (0 -> 1, weight=3)  
- (0 -> 2, weight=8)
- (1 -> 3, weight=-2)
- (2 -> 1, weight=4)

Output:​

Shortest path distances between all pairs:
From vertex 0: [0, 3, 8, 1]
From vertex 1: [inf, 0, inf, -2]
From vertex 2: [inf, 4, 0, 2]
From vertex 3: [inf, inf, inf, 0]