All-pairs shortest paths - Johnson's algorithm for sparse graphs - GeeksforGeeks


Johnson's algorithm: O(V2log V + VE) 
Time Complexity: The main steps in 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 same as Floyd Warshell when the graphs is complete (For a complete graph E = O(V2). But for sparse graphs, the algorithm performs much better than Floyd Warshell.

If we apply Dijkstra’s Single Source shortest path algorithm for every vertex, considering every vertex as source, we can find all pair shortest paths in O(V*VLogV) time. So using Dijkstra’s single source shortest path seems to be a better option than Floyd Warshell, but the problem with Dijkstra’s algorithm is, 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.

Johnson's algorithm finds shortest paths between all pairs by using both Dijkstra's algorithm and the Bellman-Ford algorithm as subroutines.

How to transform a given graph to a graph with all non-negative weight edges?
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, all set of paths between any two vertices are increased by same amount and all negative weights become non-negative. Consider any path between two vertices s and t, weight of every path is increased by h[s] – h[t], all h[] values of vertices on 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 new vertex to all existing vertices are h[] values.

How does the transformation ensure nonnegative weight edges?The following property is always true about h[] values as they are shortest distances. h[v] <= h[u] + w(u, v)

The property simply means, shortest distance from s to v must be smaller than or equal to shortest distance from s to u plus weight of 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)".

http://joonki-jeong.blogspot.com/2013/01/johnsons-algorithm.html
    Dijkstra's algorithm and the Bellman-Ford algorithm finds shortest paths from the single source vertex to others. Dijkstra's algorithm runs in O(n*lg n) to compute shortest paths while the Bellman-Ford runs in O(n*m), where n is the number of vertices and m is the number of edges. 
   
 However, Dijkstra's algorithm has one shortcoming, which is the constraint that the input graph must not have negative cost edges. On the other hand, the Bellman-Ford allows the input graph to have negative cost edges, but no negative cost cycles.

  To overcome the constraints of the two single source shortest path algorithms, Johnson's algorithm uses the technique of re-weighting. Johnson's algorithm follows the steps below.

  Given a weighted, directed graph G=(V,E) with weight function : E -> R. Weights might or might not be negative.
  1. Form G' by adding a new vertex s and a new edge (s,v) with weight 0 for each v in V.
  2. Run Bellman-Ford algorithm on G' with source vertex s
    (If B-F algorithm detects a negative cost cycle in G', halt and report this).
  3. For each v in G, let P[v] be the length of a shortest path from s to v in G'. For each edge e = (u,v) in G, define 'new weight' = 'old weight' + P[u] - P[v].
  4. For each vertex u of G:
      Run Dijkstra's algorithm in G with edge costs {new weights}, with source vertex u to compute the shortest path distance d'(u,v) for each v in G.
  5. For each pair (u,v) in G, return the shortest path distance d(u,v) = d'(u,v) - P[u] + P[v].

  (a).
  A new vertex is added to G with weight 0 to other vertices(Step 1). Then, run the Bellman-Ford algorithm on G(Step 2).

  (b).
  For each edge in G', re-weight the edge weight(Step 3). Please note that all edge weights are non-negative.

  (c) ~ (g).
  Remove newly added vertex and run Dijkstra's algorithm for each vertex v in G(Step 4).

  You will end up having the all-pair shortest paths sequences(vertices) for now, but one more step to compute the real value of the shortest paths. By step 5, you will undo re-weighting and have the original weights of shortest paths between all pairs of vertices.

  JOHNSON (G, w):
  1. compute G', where G'.V = G.V U {s},
        G'.E - G.E U {(s,v) : v in G.V} and w(s,v) = 0 for all v in G.V
  2. if BELLMAN-FORD (G', w, s) == FALSE:
  3.     print "the input graph contains a negative-weight cycle"
  4. else
  5.     for each vertex v in G'.V:
  6.         set h(v) to the value of q(s,v) computed by the Bellman-Ford algorithm.
  7.     for each edge (u,v) in G'.E:
  8.         w'(u,v) = w(u,v) + h(u) - h(v)
  9.     let D = d(u,v) be a new n x n matrix
  10.     for each vertex u in G.V:
  11.         run DIJKSTRA (G, w', u) to compute q'(u,v) for all v in G.V
  12.         for each vertex v in G.V:
  13.             d(u,v) = q'(u,v) + h(v) - h(u)
  14.     return D

Cpp complete code: https://gist.github.com/ashleyholman/6793360
http://www.keithschwarz.com/interesting/code/johnson/Johnson.java.html
    private static final Object SOURCE_NODE = new Object();
    public static <T> DirectedGraph<T> shortestPaths(DirectedGraph<T> graph) {
        /* Construct the augmented graph G' that will be fed into the Bellman-
         * Ford step by copying the graph and adding an extra node.  Because
         * the source node is of type Object (because we can't necessarily
         * create an element of type T that isn't already in the graph), this
         * graph will store plain old Objects.
         */

        DirectedGraph<Object> augmentedGraph = constructAugmentedGraph(graph);
        
        /* Compute the single-source shortest path from the source node to
         * each other node in the graph to get the potential function h.
         */

        Map<Object, Double> potential = BellmanFord.shortestPaths(augmentedGraph, SOURCE_NODE);

        /* Now, reweight the edges of the input graph by adjusting edge
         * weights based on the potential.
         */

        DirectedGraph<T> reweightedGraph = reweightGraph(graph, potential);

        /* Our return value is a graph with the all-pairs shortest paths
         * values for edges.  We'll begin by initializing it so that it has a
         * copy of each node in the source graph.
         */

        DirectedGraph<T> result = new DirectedGraph<T>();
        for (T node: graph)
            result.addNode(node);

        /* Now, run Dijkstra's algorithm over every node in the updated graph
         * to get the transformed shortest path costs.
         */

        for (T node: graph) {
            Map<T, Double> costs = Dijkstra.shortestPaths(reweightedGraph, node);

            /* We now have the shortest path costs from this node to all other
             * nodes, but the costs are using the new edges rather than the
             * old.  In particular, we have that
             *
             *                  C'(u, v) = C(u, v) + h(u) - h(v)
             *
             * When recording these costs in the new graph, we'll therefore
             * add in h(v) - h(u).
             */

            for (Map.Entry<T, Double> path: costs.entrySet())
                result.addEdge(node, path.getKey(),
                               path.getValue() + potential.get(path.getKey()) - potential.get(node));
        }

        /* Hand back the resulting graph. */
        return result;
    }
    private static <T> DirectedGraph<Object> constructAugmentedGraph(DirectedGraph<T> graph) {
        DirectedGraph<Object> result = new DirectedGraph<Object>();

        /* Copy over the nodes. */
        for (Object node: graph)
            result.addNode(node);

        /* Copy over the edges. */
        for (T node: graph)
            for (Map.Entry<T, Double> edge: graph.edgesFrom(node).entrySet())
                result.addEdge(node, edge.getKey(), edge.getValue());

        /* Add the new node to the graph. */
        result.addNode(SOURCE_NODE);

        /* Connect it to each other node with an edge of cost zero. */
        for (Object node: graph)
            result.addEdge(SOURCE_NODE, node, 0.0);

        return result;
    }urn A reweighted version of the graph.
     */

    private static <T> DirectedGraph<T> reweightGraph(DirectedGraph<T> graph,
                                                      Map<Object, Double> potential) {
        /* Begin by copying over all the nodes from the old graph. */
        DirectedGraph<T> result = new DirectedGraph<T>();
        for (T node: graph)
            result.addNode(node);

        /* Now, copy over the edge with new weights; in particular, by using
         * l'(u, v) = l(u, v) - l(v) + l(u).
         */

        for (T node: graph)
            for (Map.Entry<T, Double> edge: graph.edgesFrom(node).entrySet())
                result.addEdge(node, edge.getKey(),
                               edge.getValue() + potential.get(node) - potential.get(edge.getKey()));

        return result;
    }
Read full article from Johnson's algorithm for All-pairs shortest paths - GeeksforGeeks

Labels

LeetCode (1432) GeeksforGeeks (1122) LeetCode - Review (1067) Review (882) Algorithm (668) to-do (609) Classic Algorithm (270) Google Interview (237) Classic Interview (222) Dynamic Programming (220) DP (186) Bit Algorithms (145) POJ (141) Math (137) Tree (132) LeetCode - Phone (129) EPI (122) Cracking Coding Interview (119) DFS (115) Difficult Algorithm (115) Lintcode (115) Different Solutions (110) Smart Algorithm (104) Binary Search (96) BFS (91) HackerRank (90) Binary Tree (86) Hard (79) Two Pointers (78) Stack (76) Company-Facebook (75) BST (72) Graph Algorithm (72) Time Complexity (69) Greedy Algorithm (68) Interval (63) Company - Google (62) Geometry Algorithm (61) Interview Corner (61) LeetCode - Extended (61) Union-Find (60) Trie (58) Advanced Data Structure (56) List (56) Priority Queue (53) Codility (52) ComProGuide (50) LeetCode Hard (50) Matrix (50) Bisection (48) Segment Tree (48) Sliding Window (48) USACO (46) Space Optimization (45) Company-Airbnb (41) Greedy (41) Mathematical Algorithm (41) Tree - Post-Order (41) ACM-ICPC (40) Algorithm Interview (40) Data Structure Design (40) Graph (40) Backtracking (39) Data Structure (39) Jobdu (39) Random (39) Codeforces (38) Knapsack (38) LeetCode - DP (38) Recursive Algorithm (38) String Algorithm (38) TopCoder (38) Sort (37) Introduction to Algorithms (36) Pre-Sort (36) Beauty of Programming (35) Must Known (34) Binary Search Tree (33) Follow Up (33) prismoskills (33) Palindrome (32) Permutation (31) Array (30) Google Code Jam (30) HDU (30) Array O(N) (29) Logic Thinking (29) Monotonic Stack (29) Puzzles (29) Code - Detail (27) Company-Zenefits (27) Microsoft 100 - July (27) Queue (27) Binary Indexed Trees (26) TreeMap (26) to-do-must (26) 1point3acres (25) GeeksQuiz (25) Merge Sort (25) Reverse Thinking (25) hihocoder (25) Company - LinkedIn (24) Hash (24) High Frequency (24) Summary (24) Divide and Conquer (23) Proof (23) Game Theory (22) Topological Sort (22) Lintcode - Review (21) Tree - Modification (21) Algorithm Game (20) CareerCup (20) Company - Twitter (20) DFS + Review (20) DP - Relation (20) Brain Teaser (19) DP - Tree (19) Left and Right Array (19) O(N) (19) Sweep Line (19) UVA (19) DP - Bit Masking (18) LeetCode - Thinking (18) KMP (17) LeetCode - TODO (17) Probabilities (17) Simulation (17) String Search (17) Codercareer (16) Company-Uber (16) Iterator (16) Number (16) O(1) Space (16) Shortest Path (16) itint5 (16) DFS+Cache (15) Dijkstra (15) Euclidean GCD (15) Heap (15) LeetCode - Hard (15) Majority (15) Number Theory (15) Rolling Hash (15) Tree Traversal (15) Brute Force (14) Bucket Sort (14) DP - Knapsack (14) DP - Probability (14) Difficult (14) Fast Power Algorithm (14) Pattern (14) Prefix Sum (14) TreeSet (14) Algorithm Videos (13) Amazon Interview (13) Basic Algorithm (13) Codechef (13) Combination (13) Computational Geometry (13) DP - Digit (13) LCA (13) LeetCode - DFS (13) Linked List (13) Long Increasing Sequence(LIS) (13) Math-Divisible (13) Reservoir Sampling (13) mitbbs (13) Algorithm - How To (12) Company - Microsoft (12) DP - Interval (12) DP - Multiple Relation (12) DP - Relation Optimization (12) LeetCode - Classic (12) Level Order Traversal (12) Prime (12) Pruning (12) Reconstruct Tree (12) Thinking (12) X Sum (12) AOJ (11) Bit Mask (11) Company-Snapchat (11) DP - Space Optimization (11) Dequeue (11) Graph DFS (11) MinMax (11) Miscs (11) Princeton (11) Quick Sort (11) Stack - Tree (11) 尺取法 (11) 挑战程序设计竞赛 (11) Coin Change (10) DFS+Backtracking (10) Facebook Hacker Cup (10) Fast Slow Pointers (10) HackerRank Easy (10) Interval Tree (10) Limited Range (10) Matrix - Traverse (10) Monotone Queue (10) SPOJ (10) Starting Point (10) States (10) Stock (10) Theory (10) Tutorialhorizon (10) Kadane - Extended (9) Mathblog (9) Max-Min Flow (9) Maze (9) Median (9) O(32N) (9) Quick Select (9) Stack Overflow (9) System Design (9) Tree - Conversion (9) Use XOR (9) Book Notes (8) Company-Amazon (8) DFS+BFS (8) DP - States (8) Expression (8) Longest Common Subsequence(LCS) (8) One Pass (8) Quadtrees (8) Traversal Once (8) Trie - Suffix (8) 穷竭搜索 (8) Algorithm Problem List (7) All Sub (7) Catalan Number (7) Cycle (7) DP - Cases (7) Facebook Interview (7) Fibonacci Numbers (7) Flood fill (7) Game Nim (7) Graph BFS (7) HackerRank Difficult (7) Hackerearth (7) Inversion (7) Kadane’s Algorithm (7) Manacher (7) Morris Traversal (7) Multiple Data Structures (7) Normalized Key (7) O(XN) (7) Radix Sort (7) Recursion (7) Sampling (7) Suffix Array (7) Tech-Queries (7) Tree - Serialization (7) Tree DP (7) Trie - Bit (7) 蓝桥杯 (7) Algorithm - Brain Teaser (6) BFS - Priority Queue (6) BFS - Unusual (6) Classic Data Structure Impl (6) DP - 2D (6) DP - Monotone Queue (6) DP - Unusual (6) DP-Space Optimization (6) Dutch Flag (6) How To (6) Interviewstreet (6) Knapsack - MultiplePack (6) Local MinMax (6) MST (6) Minimum Spanning Tree (6) Number - Reach (6) Parentheses (6) Pre-Sum (6) Probability (6) Programming Pearls (6) Rabin-Karp (6) Reverse (6) Scan from right (6) Schedule (6) Stream (6) Subset Sum (6) TSP (6) Xpost (6) n00tc0d3r (6) reddit (6) AI (5) Abbreviation (5) Anagram (5) Art Of Programming-July (5) Assumption (5) Bellman Ford (5) Big Data (5) Code - Solid (5) Code Kata (5) Codility-lessons (5) Coding (5) Company - WMware (5) Convex Hull (5) Crazyforcode (5) DFS - Multiple (5) DFS+DP (5) DP - Multi-Dimension (5) DP-Multiple Relation (5) Eulerian Cycle (5) Graph - Unusual (5) Graph Cycle (5) Hash Strategy (5) Immutability (5) Java (5) LogN (5) Manhattan Distance (5) Matrix Chain Multiplication (5) N Queens (5) Pre-Sort: Index (5) Quick Partition (5) Quora (5) Randomized Algorithms (5) Resources (5) Robot (5) SPFA(Shortest Path Faster Algorithm) (5) Shuffle (5) Sieve of Eratosthenes (5) Strongly Connected Components (5) Subarray Sum (5) Sudoku (5) Suffix Tree (5) Swap (5) Threaded (5) Tree - Creation (5) Warshall Floyd (5) Word Search (5) jiuzhang (5)

Popular Posts