algorithmshortest-pathfloyd-warshall

Floyd-Warshall: all shortest paths


I've implemented Floyd-Warshall to return the distance of the shortest path between every pair of nodes/vertices and a single shortest path between each of these pairs.

Is there any way to get it to return every shortest path, even when there are multiple paths that are tied for shortest, for every pair of nodes? (I just want to know if I'm wasting my time trying)


Solution

  • If you just need the count of how many different shortest path exist, you can keep a count array in addition to the shortestPath array. Here's is a quick modification of the pseudocode from wiki.

    procedure FloydWarshall ()
        for k := 1 to n
            for i := 1 to n
                for j := 1 to n
                    if path[i][j] == path[i][k]+path[k][j] and k != j and k != i
                        count[i][j] += 1;
                    else if path[i][j] > path[i][k] + path[k][j]
                        path[i][j] = path[i][k] + path[k][j]
                        count[i][j] = 1
    

    If you need a way to find all the paths, you can store a vector/arraylist like structure for each pair to expand and collapse. Here is a modification of the pseudocode from the same wiki.

    procedure FloydWarshallWithPathReconstruction ()
        for k := 1 to n
            for i := 1 to n
                for j := 1 to n
                    if path[i][k] + path[k][j] < path[i][j]
                        path[i][j] := path[i][k]+path[k][j];
                        next[i][j].clear()
                        next[i][j].push_back(k) // assuming its a c++ vector
                    else if path[i][k] + path[k][j] == path[i][j] and path[i][j] != MAX_VALUE and k != j and k != i
                        next[i][j].push_back(k)
    

    Note: if k==j or k==i, that means, you're checking either path[i][i]+path[i][j] or path[i][j]+path[j][j], both should be equal to path[i][j] and that does not get pushed into next[i][j].

    Path reconstruction should be modified to handle the vector. The count in this case would be each vector's size. Here is a modification of the pseudocode (python) from the same wiki.

    procedure GetPath(i, j):
        allPaths = empty 2d array
        if next[i][j] is not empty:
            for every k in next[i][j]:
                if k == -1: // add the path = [i, j]
                    allPaths.add( array[ i, j] ) 
                else: // add the path = [i .. k .. j]
                    paths_I_K = GetPath(i,k) // get all paths from i to k
                    paths_K_J = GetPath(k,j) // get all paths from k to j
                    for every path between i and k, i_k in paths_I_K:
                        for every path between k and j, k_j in paths_K_J:
                            i_k = i_k.popk() // remove the last element since that repeats in k_j
                            allPaths.add( array( i_k + j_k) )
    
        return allPaths
    

    Note: path[i][j] is an adjacency list. While initializing path[i][j], you can also initialize next[i][j] by adding a -1 to the array. For instance an initialization of next[i][j] would be

    for every edge (i,j) in graph:
       next[i][j].push_back(-1)
    

    This takes care of an edge being the shortest path itself. You'll have to handle this special case in the path reconstruction, which is what i'm doing in GetPath.

    Edit: "MAX_VALUE" is the initialized value in the array of distances.