Jhonson

Introduction:

This algorithm gives shortest paths from every vertex to every other vertex using two algorithms Bellman ford and Dijkstra's shortest path algorithm

Problem Statement:

print the shortest paths from every vertex to every other vertex.

Algorithm

1. Adding a dummy new node \(q\) with zero-weighted edges to all other vertices.

2. Run \(bellman\_ford(G, q)\) and let the shortest paths be put in array \(h[\ ]\). Terminate if negative weighted cycle is detected.

3. Change the weights of edges in original graph by adding \(h[u]-h[v]\) to \(weight(u, v)\)

4. Remove vertex q and its edges with other vertices.

5. Run dijkstra for every vertex to find shortest path from every vertex to every other vertex.(stored into a 2D |V|x|V| matrix)

6. Add \(h[v]-h[u]\) to all path weights in 2D matrix.

Code :

    // shortest path between every pair of vertices
    // O(v^2logV + VE)
    // uses both dijkstra and Bellman ford
    
    #include <bits/stdc++.h>
    #include <unistd.h>
    
    using namespace std;
    
    #define adjacency_list map<int, vector<pair<int, int>>>
    #define mp make_pair
    #define INF INT_MAX
    
    typedef struct edge
    {
        int u;
        int v;
        int w;
    } Edge;
    
    typedef struct graph
    {
        adjacency_list adj;
    
        int num_edges;
        int num_vertices;
        vector<Edge> edge;
    } graph;
    
    bool bellmanford(graph G, int source, vector<int> &dist)
    {
        set<int> vertex_set;
        for (int i = 0; i < G.num_vertices; i++)
        {
            vertex_set.insert(i);
        }
        // cout << "bell--\n";
        // main part of algorithm
        dist[source] = 0;
        for (int i = 0; i < G.num_vertices - 1; i++)
        {
            for (int j = 0; j < G.num_edges; j++)
            {
                int u = G.edge[j].u;
                int v = G.edge[j].v;
                int w = G.edge[j].w;
    
                if (dist[u] != INF && dist[u] + w < dist[v])
                {
                    dist[v] = dist[u] + w;
                }
            }
        }
        // for (int i = 0; i < G.num_vertices; i++)
        // {
        //     cout << dist[i] << "\n";
        // }
        // cout << "bell--\n";
        //// cycle detection
        for (int j = 0; j < G.num_edges; j++)
        {
            int u = G.edge[j].u;
            int v = G.edge[j].v;
            int w = G.edge[j].w;
    
            if (dist[u] != INF && dist[u] + w < dist[v])
            {
                return false;
            }
        }
        return true;
    }
    
    void dijkstra(graph G, int dist[], int source)
    {
        set<int> vertex_set;
        for (int i = 0; i < G.num_vertices; i++)
        {
            vertex_set.insert(i);
        }
    
        dist[source] = 0;
        while (!vertex_set.empty())
        {
            int u = *(vertex_set.begin()); //  if these two lines are not initialised then we may end up looping on single vertex
            int min_dist = dist[u];
            for (auto i : vertex_set)
            {
                if (dist[i] < min_dist)
                {
                    u = i;
                    min_dist = dist[i];
                }
            }
            vertex_set.erase(u);
            // u has vertex with min distance from source
            // terminate dijkstra if you want min ditance to this vertex.
    
            for (auto e : G.edge)
            {
                if (e.u == u)
                {
                    if (dist[e.u] != INF && dist[e.u] + e.w < dist[e.v])
                    {
                        dist[e.v] = dist[e.u] + e.w;
                    }
                }
            }
            // for (auto i : G.adj[u])
            // {
            //     int v = i.first;
            //     int new_dist = dist[u] + i.second;
            //     if (new_dist < dist[v])
            //     {
            //         dist[v] = new_dist;
            //     }
            // }
            // cout << u << "\n";
            // sleep(1);
        }
        // for (int i = 0; i < G.num_vertices; i++)
        // {
        //     cout << dist[i] << " ";
        // }
        // cout << "\n";
    }
    
    void johnson(graph G)
    {
        // creating a new graph with new node q connected all other nodes wih 0 weighted edge
    
        int new_node = G.num_vertices;
        int orignal_num_edges = G.num_edges;
        G.num_edges += G.num_vertices;
        G.num_vertices++;
    
        for (int i = orignal_num_edges; i < G.num_edges; i++)
        {
            G.edge.push_back(Edge{new_node, i - orignal_num_edges, 0});
        }
    
        // for (auto i : G.edge)
        // {
        //     cout << i.u << " " << i.v << " " << i.w << "\n";
        // }
    
        // bellman ford part of algorithm
        // cout << "bellman ford--\n";
        vector<int> h(G.num_vertices, INF);
        if (!bellmanford(G, new_node, h))
        {
            cout << "negative edge cycle detected...\nexiting....\n";
            return;
        }
        // reweighing the edges
        // cout << "reweighting edges--\n";
        for (int i = 0; i < orignal_num_edges; i++)
        {
            G.edge[i].w += h[G.edge[i].u] - h[G.edge[i].v];
        }
        // remove new_edge
        // cout << "removing edges--\n";
        G.num_vertices--;
        G.num_edges = orignal_num_edges;
    
        // for (auto i : G.edge)
        // {
        //     cout << i.u << " " << i.v << " " << i.w << "\n";
        // }
    
        for (int i = 0; i < G.num_vertices; i++)
        {
            G.edge.pop_back();
        }
    
        // dijkstra
        // cout << "removed edges\n";
        // for (auto i : G.edge)
        // {
        //     cout << i.u << " " << i.v << " " << i.w << "\n";
        // }
    
        // cout << "dijkstra--\n";
        int dist[G.num_vertices][G.num_vertices];
        for (int i = 0; i < G.num_vertices; i++)
        {
            for (int j = 0; j < G.num_vertices; j++)
            {
                dist[i][j] = INF;
            }
        }
    
        for (int i = 0; i < G.num_vertices; i++)
        {
            dijkstra(G, &dist[i][0], i);
            // cout << "i = " << i << "\n--------------------\n";
        }
    
        for (int i = 0; i < G.num_vertices; i++)
        {
            for (int j = 0; j < G.num_vertices; j++)
            {
                dist[i][j] += h[j] - h[i];
            }
        }
    
        for (int i = 0; i < G.num_vertices; i++)
        {
            for (int j = 0; j < G.num_vertices; j++)
            {
                if (dist[i][j] == INF)
                {
                    cout << "INF  ";
                }
                else
                {
                    printf("%-4d ", dist[i][j]);
                }
            }
            cout << "\n";
        }
    }
    
    int main()
    {
        // undirected graph
    
        graph G; // u - {{v1,w1}, {v2, w2}, .... {vk,wk}}
        cin >> G.num_vertices;
        cin >> G.num_edges;
        int u, v, w;
        for (int i = 0; i < G.num_edges; i++)
        {
            cin >> u >> v >> w;
            G.edge.push_back(Edge{u, v, w});
        }
        johnson(G);
        // cout << "\n";
    }
                   
    

Complexity Analysis :

Time complexity : \(O(|V|.|E| + |V|(dijkstra\_complexity))\)

Time complexity (normal dijkstra without min-heaps or fibonacci heaps): \(O(|V|.|E| + |V|^3)\) ≈ \(O(|V|^3)\)

Time complexity ( dijkstra with min-heaps or fibonacci heaps): \(O(|V|.|E| + |V|^2log|V|)\) which is better than Floyd warshall.

video resources

web resources

Jhonson wikipedia

book resources