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 discussedFloyd 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 bothDijkstra andBellman-Ford as subroutines. If we applyDijkstra'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 thanFloyd 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 (Seethis 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 theBellman-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 runDijkstra'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.
C++#include<iostream>#include<vector>#include<limits>#include<algorithm>#define INF std::numeric_limits<int>::max()usingnamespacestd;// Function to find the vertex with the minimum distance// that has not yet been included in the shortest path treeintMin_Distance(constvector<int>&dist,constvector<bool>&visited){intmin=INF,min_index;for(intv=0;v<dist.size();++v){if(!visited[v]&&dist[v]<=min){min=dist[v];min_index=v;}}returnmin_index;}// Function to perform Dijkstra's algorithm on the modified graphvoidDijkstra_Algorithm(constvector<vector<int>>&graph,constvector<vector<int>>&altered_graph,intsource){intV=graph.size();// Number of verticesvector<int>dist(V,INF);// Distance from source to each vertexvector<bool>visited(V,false);// Track visited verticesdist[source]=0;// Distance to source itself is 0// Compute shortest path for all verticesfor(intcount=0;count<V-1;++count){// Select the vertex with the minimum distance that hasn't been visitedintu=Min_Distance(dist,visited);visited[u]=true;// Mark this vertex as visited// Update the distance value of the adjacent vertices of the selected vertexfor(intv=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 sourcecout<<"Shortest Distance from vertex "<<source<<":\n";for(inti=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 verticesvector<int>BellmanFord_Algorithm(constvector<vector<int>>&edges,intV){vector<int>dist(V+1,INF);// Distance from source to each vertexdist[V]=0;// Distance to the new source vertex (added vertex) is 0// Add a new source vertex to the graph and connect it to all original vertices with 0 weight edgesvector<vector<int>>edges_with_extra(edges);for(inti=0;i<V;++i){edges_with_extra.push_back({V,i,0});}// Relax all edges |V| - 1 timesfor(inti=0;i<V;++i){for(constauto&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];}}}returnvector<int>(dist.begin(),dist.begin()+V);// Return distances excluding the new source vertex}// Function to implement Johnson's AlgorithmvoidJohnsonAlgorithm(constvector<vector<int>>&graph){intV=graph.size();// Number of verticesvector<vector<int>>edges;// Collect all edges from the graphfor(inti=0;i<V;++i){for(intj=0;j<V;++j){if(graph[i][j]!=0){edges.push_back({i,j,graph[i][j]});}}}// Get the modified weights from Bellman-Ford algorithmvector<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 weightsfor(inti=0;i<V;++i){for(intj=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 edgescout<<"Modified Graph:\n";for(constauto&row:altered_graph){for(intweight:row){cout<<weight<<' ';}cout<<endl;}// Run Dijkstra's algorithm for every vertex as the sourcefor(intsource=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 implementationintmain(){// Define a graph with possible negative weightsvector<vector<int>>graph={{0,-5,2,3},{0,0,4,0},{0,0,0,1},{0,0,0,0}};// Execute Johnson's AlgorithmJohnsonAlgorithm(graph);return0;}Javaimportjava.util.Arrays;publicclassGFG{// Define infinity as a large integer valueprivatestaticfinalintINF=Integer.MAX_VALUE;// Function to find the vertex with the minimum distance// from the source that has not yet been included in the shortest path treeprivatestaticintminDistance(int[]dist,boolean[]sptSet){intmin=INF,minIndex=0;for(intv=0;v<dist.length;v++){// Update minIndex if a smaller distance is foundif(!sptSet[v]&&dist[v]<=min){min=dist[v];minIndex=v;}}returnminIndex;}// Function to perform Dijkstra's algorithm on the modified graphprivatestaticvoiddijkstraAlgorithm(int[][]graph,int[][]alteredGraph,intsource){intV=graph.length;// Number of verticesint[]dist=newint[V];// Distance array to store shortest distance from sourceboolean[]sptSet=newboolean[V];// Boolean array to track visited vertices// Initialize distances with infinity and source distance as 0Arrays.fill(dist,INF);dist[source]=0;// Compute shortest path for all verticesfor(intcount=0;count<V-1;count++){// Pick the vertex with the minimum distance that hasn't been visitedintu=minDistance(dist,sptSet);sptSet[u]=true;// Mark this vertex as visited// Update distance values for adjacent verticesfor(intv=0;v<V;v++){// Check for updates to the distance valueif(!sptSet[v]&&graph[u][v]!=0&&dist[u]!=INF&&dist[u]+alteredGraph[u][v]<dist[v]){dist[v]=dist[u]+alteredGraph[u][v];}}}// Print the shortest distances from the source vertexSystem.out.println("Shortest Distance from vertex "+source+":");for(inti=0;i<V;i++){System.out.println("Vertex "+i+": "+(dist[i]==INF?"INF":dist[i]));}}// Function to perform Bellman-Ford algorithm to calculate shortest distances// from a source vertex to all other verticesprivatestaticint[]bellmanFordAlgorithm(int[][]edges,intV){int[]dist=newint[V+1];// Distance array with an extra vertexArrays.fill(dist,INF);dist[V]=0;// Distance to the new source vertex (added vertex) is 0// Add edges from the new source vertex to all original verticesint[][]edgesWithExtra=Arrays.copyOf(edges,edges.length+V);for(inti=0;i<V;i++){edgesWithExtra[edges.length+i]=newint[]{V,i,0};}// Relax all edges |V| - 1 timesfor(inti=0;i<V;i++){for(int[]edge:edgesWithExtra){if(dist[edge[0]]!=INF&&dist[edge[0]]+edge[2]<dist[edge[1]]){dist[edge[1]]=dist[edge[0]]+edge[2];}}}returnArrays.copyOf(dist,V);// Return distances excluding the new source vertex}// Function to implement Johnson's AlgorithmprivatestaticvoidjohnsonAlgorithm(int[][]graph){intV=graph.length;// Number of verticesint[][]edges=newint[V*(V-1)/2][3];// Array to store edgesintindex=0;// Collect all edges from the graphfor(inti=0;i<V;i++){for(intj=0;j<V;j++){if(graph[i][j]!=0){edges[index++]=newint[]{i,j,graph[i][j]};}}}// Get the modified weights to remove negative weights using Bellman-Fordint[]alteredWeights=bellmanFordAlgorithm(edges,V);int[][]alteredGraph=newint[V][V];// Modify the weights of the edges to ensure all weights are non-negativefor(inti=0;i<V;i++){for(intj=0;j<V;j++){if(graph[i][j]!=0){alteredGraph[i][j]=graph[i][j]+alteredWeights[i]-alteredWeights[j];}}}// Print the modified graph with re-weighted edgesSystem.out.println("Modified Graph:");for(int[]row:alteredGraph){for(intweight:row){System.out.print(weight+" ");}System.out.println();}// Run Dijkstra's algorithm for each vertex as the sourcefor(intsource=0;source<V;source++){System.out.println("\nShortest Distance with vertex "+source+" as the source:");dijkstraAlgorithm(graph,alteredGraph,source);}}// Main function to test Johnson's Algorithmpublicstaticvoidmain(String[]args){// Define a graph with possible negative weightsint[][]graph={{0,-5,2,3},{0,0,4,0},{0,0,0,1},{0,0,0,0}};// Execute Johnson's AlgorithmjohnsonAlgorithm(graph);}}Python# Implementation of Johnson's algorithm in Python3# Import function to initialize the dictionaryfromcollectionsimportdefaultdictINT_MAX=float('Inf')# Function that returns the vertex# with minimum distance# from the sourcedefMin_Distance(dist,visit):(minimum,Minimum_Vertex)=(INT_MAX,0)forvertexinrange(len(dist)):ifminimum>dist[vertex]andvisit[vertex]==False:(minimum,minVertex)=(dist[vertex],vertex)returnMinimum_Vertex# Dijkstra Algorithm for Modified# Graph (After removing the negative weights)defDijkstra_Algorithm(graph,Altered_Graph,source):# Number of vertices in the graphtot_vertices=len(graph)# Dictionary to check if given vertex is# already included in the shortest path treesptSet=defaultdict(lambda:False)# Shortest distance of all vertices from the sourcedist=[INT_MAX]*tot_verticesdist[source]=0forcountinrange(tot_vertices):# The current vertex which is at min Distance# from the source and not yet included in the# shortest path treecurVertex=Min_Distance(dist,sptSet)sptSet[curVertex]=Trueforvertexinrange(tot_vertices):if((sptSet[vertex]==False)and(dist[vertex]>(dist[curVertex]+Altered_Graph[curVertex][vertex]))and(graph[curVertex][vertex]!=0)):dist[vertex]=(dist[curVertex]+Altered_Graph[curVertex][vertex])# Print the Shortest distance from the sourceforvertexinrange(tot_vertices):print('Vertex '+str(vertex)+': '+str(dist[vertex]))# Function to calculate shortest distances from source# to all other vertices using Bellman-Ford algorithmdefBellmanFord_Algorithm(edges,graph,tot_vertices):# Add a source s and calculate its min# distance from every other nodedist=[INT_MAX]*(tot_vertices+1)dist[tot_vertices]=0foriinrange(tot_vertices):edges.append([tot_vertices,i,0])foriinrange(tot_vertices):for(source,destn,weight)inedges:if((dist[source]!=INT_MAX)and(dist[source]+weight<dist[destn])):dist[destn]=dist[source]+weight# Don't send the value for the source addedreturndist[0:tot_vertices]# Function to implement Johnson AlgorithmdefJohnsonAlgorithm(graph):edges=[]# Create a list of edges for Bellman-Ford Algorithmforiinrange(len(graph)):forjinrange(len(graph[i])):ifgraph[i][j]!=0:edges.append([i,j,graph[i][j]])# Weights used to modify the original weightsAlter_weigts=BellmanFord_Algorithm(edges,graph,len(graph))Altered_Graph=[[0forpinrange(len(graph))]forqinrange(len(graph))]# Modify the weights to get rid of negative weightsforiinrange(len(graph)):forjinrange(len(graph[i])):ifgraph[i][j]!=0:Altered_Graph[i][j]=(graph[i][j]+Alter_weigts[i]-Alter_weigts[j]);print('Modified Graph: '+str(Altered_Graph))# Run Dijkstra for every vertex as source one by oneforsourceinrange(len(graph)):print('\nShortest Distance with vertex '+str(source)+' as the source:\n')Dijkstra_Algorithm(graph,Altered_Graph,source)# Driver Codegraph=[[0,-5,2,3],[0,0,4,0],[0,0,0,1],[0,0,0,0]]JohnsonAlgorithm(graph)JavaScriptconstINF=Number.MAX_VALUE;// Function to find the vertex with minimum distance from the sourcefunctionminDistance(dist,visited){letmin=INF;letminIndex=-1;for(letv=0;v<dist.length;v++){if(!visited[v]&&dist[v]<min){min=dist[v];minIndex=v;}}returnminIndex;}// Function to perform Dijkstra's algorithm on the modified graphfunctiondijkstraAlgorithm(graph,alteredGraph,source){constV=graph.length;constdist=Array(V).fill(INF);constvisited=Array(V).fill(false);dist[source]=0;for(letcount=0;count<V-1;count++){constu=minDistance(dist,visited);visited[u]=true;for(letv=0;v<V;v++){if(!visited[v]&&graph[u][v]!==0&&dist[u]!==INF&&dist[u]+alteredGraph[u][v]<dist[v]){dist[v]=dist[u]+alteredGraph[u][v];}}}console.log(`Shortest Distance from vertex${source}:`);for(leti=0;i<V;i++){console.log(`Vertex${i}:${dist[i]===INF?"INF":dist[i]}`);}}// Function to perform Bellman-Ford algorithm to calculate shortest distancesfunctionbellmanFordAlgorithm(edges,V){constdist=Array(V+1).fill(INF);dist[V]=0;constedgesWithExtra=edges.slice();for(leti=0;i<V;i++){edgesWithExtra.push([V,i,0]);}for(leti=0;i<V;i++){for(const[src,dest,weight]ofedgesWithExtra){if(dist[src]!==INF&&dist[src]+weight<dist[dest]){dist[dest]=dist[src]+weight;}}}returndist.slice(0,V);}// Function to implement Johnson's AlgorithmfunctionjohnsonAlgorithm(graph){constV=graph.length;constedges=[];for(leti=0;i<V;i++){for(letj=0;j<V;j++){if(graph[i][j]!==0){edges.push([i,j,graph[i][j]]);}}}constalteredWeights=bellmanFordAlgorithm(edges,V);constalteredGraph=Array.from({length:V},()=>Array(V).fill(0));for(leti=0;i<V;i++){for(letj=0;j<V;j++){if(graph[i][j]!==0){alteredGraph[i][j]=graph[i][j]+alteredWeights[i]-alteredWeights[j];}}}console.log("Modified Graph:");alteredGraph.forEach(row=>{console.log(row.join(' '));});for(letsource=0;source<V;source++){console.log(`\nShortest Distance with vertex${source} as the source:`);dijkstraAlgorithm(graph,alteredGraph,source);}}// Driver Codeconstgraph=[[0,-5,2,3],[0,0,4,0],[0,0,0,1],[0,0,0,0]];johnsonAlgorithm(graph);OutputFollowing matrix shows the shortest distances between every pair of vertices 0 5 8 9 INF 0 3 4 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 asFloyd 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)
Explore
DSA Fundamentals
Data Structures
Algorithms
Advanced
Interview Preparation
Practice Problem