Algorithm
Plain Dijkstra:
- dense graphs
- sparse graphs
Running Dijkstra for all nodes we get:
- dense graphs
- sparse graphs
For sparse graphs Dijkstra is faster than Floyd-Warshall:
Johnson’s algorithm uses Dijkstra, but since Dijkstra works only with non-negative weights we need a method to make all weights positive. One way would be to find the shortest edge and add it to all the other edges and then subtract it from the shortest paths. But, this method doesn’t work in all cases since there maybe multiple paths from (let’s say) node
So, Johnson’s algorithm have the following idea: reweight every node so that
Johnson’s algorithm main steps:
- Find the function
such that for all or determine that has negative cycles. - Run Dijkstra on
for every node and find for all . - Given
and compute for all .
Correctness
Claim 1: Lets assume we know
Basically, we claim that
Let
Hence, any path
Claim 2: If
Let
…
Adding these inequalities, we get:
Claim 3: If we add a new node (let’s denoted it
The Johnson’s algorithm:
- Add new node
to and add an edge of weight to every node and compute for every node using Bellman-Ford. If there is a negative cycle, then Bellman-Ford will detect it. - Re-weight each edge with
. - Run Dijkstra from every node
and compute all shortest paths. - Re-weight each edge with
.
Done.
Implementation
tuple<bool, vector<vector<int>>>
johnson(int n, vector<vector<pair<int, int>>>& adjL) {
vector<vector<int>> d(n, vector<int>(n));
bool cycle = false;
// add new vertex (n) and edges with weight 0 to every other vertex
vector<pair<int, int>> edges(n);
for (int u = 0; u < n; u++) edges[u] = make_pair(u, 0);
adjL.push_back(edges);
// Bellman-Ford and return function h
vector<int> h;
tie (cycle, h) = bellmanFord(n, n + 1, adjL);
if (cycle) return make_pair(cycle, d);
// remove vertex (n)
h.pop_back(); adjL.pop_back();
// Dijkstra on modified edges
for (int u = 0; u < n; u++) {
d[u] = dijkstra(u, n, adjL, h);
// compute real distance
for (int v = 0; v < n; v++)
if (d[u][v] != INF) // is there a path
d[u][v] = d[u][v] + (h[v] - h[u]);
}
return make_pair(cycle, d);
}
tuple<bool, vector<int>>
bellmanFord(int start, int n, vector<vector<pair<int, int>>>& adjL) {
bool cycle = false;
vector<int> d(n, INF);
d[start] = 0;
for (int k = 1; k <= n - 1; k++) {
for (int u = 0; u < n; u++) {
if (d[u] == INF) continue;
for (pair<int, int> p : adjL[u]) {
int v = p.first;
int w = p.second;
d[v] = min(d[v], d[u] + w); // relaxation
}
}
}
// check for cycles
for (int u = 0; u < n; u++) {
if (d[u] == INF) continue;
for (pair<int, int> p : adjL[u]) {
int v = p.first;
int w = p.second;
if (d[v] > d[u] + w) {
cycle = true; break;
}
}
if (cycle) break;
}
return make_pair(cycle, d);
}
vector<int>
dijkstra(int start, int n, vector<vector<pair<int, int>>>& adjL, vector<int>& h) {
vector<bool> s(n, false);
vector<int> d(n, INF);
d[start] = 0;
apqmin<int> q; // min priority queue
for (int u = 0; u < n; u++) q.push(u, d[u]);
while (!q.empty()) {
int u = q.front(); q.pop();
s[u] = true;
if (d[u] == INF) continue;
for (pair<int, int> p : adjL[u]) {
int v = p.first;
int w = p.second + (h[u] - h[v]);
if (s[v] == true) continue;
if (d[v] > d[u] + w) { // relaxation
d[v] = d[u] + w;
q.update(v, d[v]); // decrease priority
}
}
}
return d;
}
Complexity