Johnson’s Algorithm is an algorithm used to find the shortest paths between all pairs of vertices in a weighted graph. It is especially useful for sparse graphs and can handle negative weights, provided there are no negative weight cycles. This algorithm uses both Bellman-Ford and Dijkstra's algorithms to achieve efficient results.
In this article, we will learn Johnson’s Algorithm and demonstrate how to implement it in C++.
Johnson’s Algorithm transforms the original graph to ensure all edge weights are non-negative, making it possible to use Dijkstra’s algorithm. The transformation involves adding a new vertex, computing a potential function using Bellman-Ford, re-weighting the edges, and then running Dijkstra’s algorithm from each vertex.
Steps to Implement Johnson’s Algorithm in C++
- 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.
Working of Johnson Algorithm in C++
Consider the below example to understand step-by-step implementation of Johnson’s Algorithm.
Step 1: Add a New Vertex
We add a new vertex 4 and connect it to all other vertices with edges of weight 0.
Step 2: Compute Potential Function using Bellman-Ford
Using the Bellman-Ford algorithm from vertex 4, we compute the shortest path estimates (potential function):
Distances from vertex 4 to vertices 0, 1, 2, and 3 are 0, −5, −1, and 0, respectively.
Therefore, h[]={0,−5,−1,0}
Step 3: Re-weight the Edges
Re-weight the edges using the formula
w′(u,v) = w(u,v) + h[u] − h[v]
After re-weighting, the edges are as follows:
Edge (0,1): Original weight = −5, New weight = −5+0−(−5) = 0
Edge (1,2): Original weight = 4, New weight = 4+(−5)−(−1) = 0
Edge (2,3): Original weight = 1, New weight = 1+(−1)−0 = 0
Edge (0,3): Original weight = 3, New weight = 3+0−0 = 3
Edge (3,2): Original weight = 2, New weight = 2+0−(−1) = 3
Step 4: Run Dijkstra's Algorithm
Run Dijkstra's algorithm from each vertex in the re-weighted graph to find the shortest paths.
Since all re-weighted edge weights are non-negative, Dijkstra's algorithm can be used efficiently.
C++ Program to Implement Johnson’s Algorithm
The below program illustrate the implementation of Johnson’s Algorithm in C++.
C++
// C++ program to implement Johnson's Algorithm for finding the shortest paths
// between all pairs of vertices in a graph that may contain negative weights.
#include <algorithm>
#include <iostream>
#include <limits>
#include <vector>
#define INF numeric_limits<int>::max()
using namespace std;
// Function to find the vertex with the minimum distance that has not yet been included in the shortest path
// tree
int Min_Distance(const vector<int> &dist, const vector<bool> &visited)
{
int min = INF, min_index;
for (int v = 0; v < dist.size(); ++v)
{
if (!visited[v] && dist[v] <= min)
{
min = dist[v];
min_index = v;
}
}
return min_index;
}
// Function to perform Dijkstra's algorithm on the modified graph
void Dijkstra_Algorithm(const vector<vector<int>> &graph, const vector<vector<int>> &altered_graph,
int source)
{
// Number of vertices
int V = graph.size();
// Distance from source to each vertex
vector<int> dist(V, INF);
// Track visited vertices
vector<bool> visited(V, false);
// Distance to source itself is 0
dist[source] = 0;
// Compute shortest path for all vertices
for (int count = 0; count < V - 1; ++count)
{
// Select the vertex with the minimum distance that hasn't been visited
int u = Min_Distance(dist, visited);
// Mark this vertex as visited
visited[u] = true;
// Update the distance value of the adjacent vertices of the selected vertex
for (int v = 0; v < V; ++v)
{
if (!visited[v] && graph[u][v] != 0 && dist[u] != INF && dist[u] + altered_graph[u][v] < dist[v])
{
dist[v] = dist[u] + altered_graph[u][v];
}
}
}
// Print the shortest distances from the source
cout << "Shortest Distance from vertex " << source << ":\n";
for (int i = 0; i < V; ++i)
{
cout << "Vertex " << i << ": " << (dist[i] == INF ? "INF" : to_string(dist[i])) << endl;
}
}
// Function to perform Bellman-Ford algorithm to find shortest distances
// from a source vertex to all other vertices
vector<int> BellmanFord_Algorithm(const vector<vector<int>> &edges, int V)
{
// Distance from source to each vertex
vector<int> dist(V + 1, INF);
// Distance to the new source vertex (added vertex) is 0
dist[V] = 0;
// Add a new source vertex to the graph and connect it to all original vertices with 0 weight edges
vector<vector<int>> edges_with_extra(edges);
for (int i = 0; i < V; ++i)
{
edges_with_extra.push_back({V, i, 0});
}
// Relax all edges |V| - 1 times
for (int i = 0; i < V; ++i)
{
for (const auto &edge : edges_with_extra)
{
if (dist[edge[0]] != INF && dist[edge[0]] + edge[2] < dist[edge[1]])
{
dist[edge[1]] = dist[edge[0]] + edge[2];
}
}
}
// Return distances excluding the new source vertex
return vector<int>(dist.begin(), dist.begin() + V);
}
// Function to implement Johnson's Algorithm
void JohnsonAlgorithm(const vector<vector<int>> &graph)
{
// Number of vertices
int V = graph.size();
vector<vector<int>> edges;
// Collect all edges from the graph
for (int i = 0; i < V; ++i)
{
for (int j = 0; j < V; ++j)
{
if (graph[i][j] != 0)
{
edges.push_back({i, j, graph[i][j]});
}
}
}
// Get the modified weights from Bellman-Ford algorithm
vector<int> altered_weights = BellmanFord_Algorithm(edges, V);
vector<vector<int>> altered_graph(V, vector<int>(V, 0));
// Modify the weights of the edges to remove negative weights
for (int i = 0; i < V; ++i)
{
for (int j = 0; j < V; ++j)
{
if (graph[i][j] != 0)
{
altered_graph[i][j] = graph[i][j] + altered_weights[i] - altered_weights[j];
}
}
}
// Print the modified graph with re-weighted edges
cout << "Modified Graph:\n";
for (const auto &row : altered_graph)
{
for (int weight : row)
{
cout << weight << ' ';
}
cout << endl;
}
// Run Dijkstra's algorithm for every vertex as the source
for (int source = 0; source < V; ++source)
{
cout << "\nShortest Distance with vertex " << source << " as the source:\n";
Dijkstra_Algorithm(graph, altered_graph, source);
}
}
// Main function to test the Johnson's Algorithm implementation
int main()
{
// Define a graph with possible negative weights
vector<vector<int>> graph = {{0, -5, 2, 3}, {0, 0, 4, 0}, {0, 0, 0, 1}, {0, 0, 0, 0}};
// Execute Johnson's Algorithm
JohnsonAlgorithm(graph);
return 0;
}
OutputModified 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:
Shortest Distance from vertex 0:
Vertex 0: 0
Vertex 1: 0
Vertex 2: 0
Vertex 3: 0
Shortest Distance with vertex 1 as the source:
Shortest Distance from vertex 1:
Vertex 0: INF
Vertex 1: 0
Vertex 2: 0
Vertex 3: 0
Shortest Distance with vertex 2 as the source:
Shortest Distance from vertex 2:
Vertex 0: INF
Vertex 1: INF
Vertex 2: 0
Vertex 3: 0
Shortest Distance with vertex 3 as the source:
Shortest Distance from vertex 3:
Vertex 0: INF
Vertex 1: INF
Vertex 2: INF
Vertex 3: 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(VLogV). 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)
Similar Reads
C++ Programming Language C++ is a computer programming language developed by Bjarne Stroustrup as an extension of the C language. It is known for is fast speed, low level memory management and is often taught as first programming language. It provides:Hands-on application of different programming concepts.Similar syntax to
5 min read
Non-linear Components In electrical circuits, Non-linear Components are electronic devices that need an external power source to operate actively. Non-Linear Components are those that are changed with respect to the voltage and current. Elements that do not follow ohm's law are called Non-linear Components. Non-linear Co
11 min read
Spring Boot Tutorial Spring Boot is a Java framework that makes it easier to create and run Java applications. It simplifies the configuration and setup process, allowing developers to focus more on writing code for their applications. This Spring Boot Tutorial is a comprehensive guide that covers both basic and advance
10 min read
Object Oriented Programming in C++ Object Oriented Programming - As the name suggests uses objects in programming. Object-oriented programming aims to implement real-world entities like inheritance, hiding, polymorphism, etc. in programming. The main aim of OOP is to bind together the data and the functions that operate on them so th
5 min read
Class Diagram | Unified Modeling Language (UML) A UML class diagram is a visual tool that represents the structure of a system by showing its classes, attributes, methods, and the relationships between them. It helps everyone involved in a projectâlike developers and designersâunderstand how the system is organized and how its components interact
12 min read
3-Phase Inverter An inverter is a fundamental electrical device designed primarily for the conversion of direct current into alternating current . This versatile device , also known as a variable frequency drive , plays a vital role in a wide range of applications , including variable frequency drives and high power
13 min read
Backpropagation in Neural Network Back Propagation is also known as "Backward Propagation of Errors" is a method used to train neural network . Its goal is to reduce the difference between the modelâs predicted output and the actual output by adjusting the weights and biases in the network.It works iteratively to adjust weights and
9 min read
30 OOPs Interview Questions and Answers [2025 Updated] Object-oriented programming, or OOPs, is a programming paradigm that implements the concept of objects in the program. It aims to provide an easier solution to real-world problems by implementing real-world entities such as inheritance, abstraction, polymorphism, etc. in programming. OOPs concept is
15 min read
What is Vacuum Circuit Breaker? A vacuum circuit breaker is a type of breaker that utilizes a vacuum as the medium to extinguish electrical arcs. Within this circuit breaker, there is a vacuum interrupter that houses the stationary and mobile contacts in a permanently sealed enclosure. When the contacts are separated in a high vac
13 min read
Polymorphism in Java Polymorphism in Java is one of the core concepts in object-oriented programming (OOP) that allows objects to behave differently based on their specific class type. The word polymorphism means having many forms, and it comes from the Greek words poly (many) and morph (forms), this means one entity ca
7 min read