Floyd-Warshall: all shortest paths

前端 未结 2 1421
广开言路
广开言路 2021-02-04 07:47

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 the

相关标签:
2条回答
  • 2021-02-04 08:38

    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.

    0 讨论(0)
  • 2021-02-04 08:47

    The 'counting' function in the current approved answer flails in some cases. A more complete solution would be:

    procedure FloydWarshallWithCount ()
    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]
                    count[i][j] += count[i][k] * count[k][j]
                else if path[i][j] > path[i][k] + path[k][j]
                    path[i][j] = path[i][k] + path[k][j]
                    count[i][j] = count[i][k] * count[k][j]
    

    The reason for this is that for any three vertices i, j, and k, there may be multiple shortest paths that run from i through k to j. For instance in the graph:

           3             1
    (i) -------> (k) ---------> (j)
     |            ^
     |            |
     | 1          | 1
     |     1      |
    (a) -------> (b)
    

    Where there are two paths from i to j through k. count[i][k] * count[k][j] finds the number of paths from i to k, and the number of paths from k to j, and multiplies them to find the number of paths i -> k -> j.

    0 讨论(0)
提交回复
热议问题