All-Pairs Shortest Paths – Floyd Warshall Algorithm

来源:互联网 发布:兰州李知女人 编辑:程序博客网 时间:2024/06/05 22:42

所有节点对之间的最短路问题 - 弗洛伊德·沃歇尔算法

给定加权图中的一组顶点V,其边缘权重w(u,v)可以为负,从图中出现的所有顶点v的每个源找到最短路径权重d(s,v)。如果图表包含负重循环,则报告。

例如,考虑下面的输入图 -

弗洛伊德·沃歇尔1

输出:

包含最短距离的邻接矩阵为 - 0 -1 -2 04 0 2 45 1 0 23 -1 1 0    Shortest Path from vertex 0 to vertex 1 is (0 2 3 1)    Shortest Path from vertex 0 to vertex 2 is (0 2)    Shortest Path from vertex 0 to vertex 3 is (0 2 3)    Shortest Path from vertex 1 to vertex 0 is (1 0)    Shortest Path from vertex 1 to vertex 2 is (1 0 2)    Shortest Path from vertex 1 to vertex 3 is (1 0 2 3)    Shortest Path from vertex 2 to vertex 0 is (2 3 1 0)    Shortest Path from vertex 2 to vertex 1 is (2 3 1)    Shortest Path from vertex 2 to vertex 3 is (2 3)    Shortest Path from vertex 3 to vertex 0 is (3 1 0)    Shortest Path from vertex 3 to vertex 1 is (3 1)    Shortest Path from vertex 3 to vertex 2 is (3 1 0 2)    

我们已经在单独的帖子中涵盖了单源最短路径。我们已经看到了

对于具有非负边缘权重的图,Dijkstra算法运行在O(E + V lg V)

对于包含负边缘权重的图表,Bellman-Ford运行在O(V.E)中。

对于DAG,Bellman-Ford的一次通行(称为放松步骤)足以使O(V + E)时间。

这里,V是顶点数,E是图中的边数。

在这篇文章中,我们将介绍全对最短路径,返回图中可能包含负边权重的每个顶点之间的最短路径。

如果图只包含正边权重,一个简单的解决方案就是运行Dijkstra算法V次。该解的时间复杂度为O(V(E + V lg V)),即O(VE + V2 lg V)

如果图形包含负边缘权重,则要找到所有对的最短路径,我们可以从每个顶点运行一次Bellman-Ford。这种方法的时间复杂度将是O(V2E)。如果曲线图密集,即E = V2,则时间复杂度变为O(V4)。

Floyd-Warshall算法是一种用于在正或负边缘权重(但没有负周期)的加权图中找到最短路径的算法。它通过比较每对顶点之间的图形的所有可能路径以及图中的O(V3)比较也是如此。

以下是维基百科给出的Bellman-Ford的psedocode。实现采用由邻接矩阵表示并以最短路径(最低成本)信息填充dist []的图,

使dist成为初始化为无穷大的最小距离的V x V矩阵

对于每个顶点v

dist [v] [v] = 0

对于每个边(u,v)

dist [u] [v] = weight(u,v)

对于k从0到| V | - 1

因为我从0到| V | - 1    对于j从0到| V | - 1            如果(dist [i] [k] + dist [k] [j])小于dist [i] [j]            dist [i] [j] = dist [i] [k] + dist [k] [j]

psedocode上面从0到V-1逐个选取顶点k,并将该顶点作为图中每对边缘i-> j之间的最短路径中的中间顶点。每当我们从i到j通过顶点k找到一个较短的路径时,我们更新成本矩阵。因为对于给定的k,我们已经将顶点[0..k-1]视为中间顶点,这种方法起作用。

让我们考虑上图,

在k的外部for循环的第一次迭代之前,唯一已知的路径对应于图形中的单个边缘。

####  弗洛伊德·沃歇尔2

在k = 0处,找到通过顶点0的路径:特别地,找到路径[1,0,2],替换具有较少边缘但是成本高的路径[1,2]。

####  弗洛伊德·沃瑟尔3

在k = 1,找到通过顶点{0,1}的路径。下图显示了在先前迭代中遇到的两个已知路径[3,1]和[1,0,2]组合路径[3,1,0,2],交点中有1个。不考虑路径[3,1,2],因为[1,0,2]是从1到2的最短路径。

####  弗洛伊德·沃歇尔4

在k = 2处,找到通过顶点{0,1,2}的路径。

####  弗洛伊德·沃歇尔5

最后,在k = 3,找到所有最短路径。

####  弗洛伊德·沃歇尔6

Floyd-Warshall算法在编程中非常简单,在实践中非常有效。它也可用于查找图形的传递闭包,并在图中检测负重量循环。

为了使用Floyd-Warshall算法检测负周期,我们需要对距离矩阵的检查对角线存在负数,因为它表示该图包含至少一个负循环。

这是如何工作的?

Floyd-Warshall算法迭代地修改所有顶点对(i,j)之间的路径长度,其中i = j。最初,路径(i,i)的长度为零。如果路径[i,k … i]具有小于零的长度,即表示负循环,则只能改善路径。因此,在算法之后,如果存在从i回到i的负长度路径,则(i,i)将为负。

C++

#define N 4//递归函数从源顶点v打印给定顶点u的路径void printPath(int path[][N], int v, int u){    if (path[v][u] == v)        return;    printPath(path, v, path[v][u]);    cout << path[v][u] << " ";}     //使用所有顶点对之间的路径信息打印最短成本的功能    void printSolution(int cost[N][N], int path[N][N]){    for (int v = 0; v < N; v++)    {        for (int u = 0; u < N; u++)        {            if (cost[v][u] == INT_MAX)                cout << setw(5) << "inf";            else                cout << setw(5) << cost[v][u];        }        cout << endl;    }    cout << endl;    for (int v = 0; v < N; v++)     {        for (int u = 0; u < N; u++)         {            if (u != v && path[v][u] != -1)             {                cout << "Shortest Path from vertex " << v <<                      " to vertex " << u << " is (" << v << " ";                printPath(path, v, u);                cout << u << ")" << endl;            }        }    }}//运行Floyd-Warshall算法的函数void FloydWarshell(int adjMatrix[][N]){    // cost[] and parent[] stores shortest-path     // (shortest-cost/shortest route) information    int cost[N][N], path[N][N];    // 初始化 cost[] and parent[]    for (int v = 0; v < N; v++)     {        for (int u = 0; u < N; u++)         {            //初始成本与边缘的重量相同            cost[v][u] = adjMatrix[v][u];            if (v == u)                path[v][u] = 0;            else if (cost[v][u] != INT_MAX)                path[v][u] = v;            else                path[v][u] = -1;        }    }   //运行Floyd-Warshell    for (int k = 0; k < N; k++)     {        for (int v = 0; v < N; v++)         {            for (int u = 0; u < N; u++)             {                 //如果顶点k在从v到u的最短路径上,则更新cost [v] [u],path [v] [u]                if (cost[v][k] != INT_MAX && cost[k][u] != INT_MAX                    && cost[v][k] + cost[k][u] < cost[v][u])                 {                    cost[v][u] = cost[v][k] + cost[k][u];                    path[v][u] = path[k][u];                }            }            //如果对角线元素变为负值,则图形包含负权重周期            if (cost[v][v] < 0)             {                cout << "Negative Weight Cycle Found!!";                return;            }        }    }//打印所有顶点对之间的最短路径    printSolution(cost, path);}// main functionint main(){    //给出矩阵的邻接表示    int adjMatrix[N][N] =    {         {       0, INT_MAX,      -2, INT_MAX },        {       4,       0,       3, INT_MAX },        { INT_MAX, INT_MAX,       0,       2 },        { INT_MAX,      -1, INT_MAX,       0 }     };    //运行Floyd Warshell算法    FloydWarshell(adjMatrix);    return 0;}Download   Run CodeOutput: Adjacency matrix containing shortest distance is –0 -1 -2 04 0 2 45 1 0 23 -1 1 0Shortest Path from vertex 0 to vertex 1 is (0 2 3 1)Shortest Path from vertex 0 to vertex 2 is (0 2)Shortest Path from vertex 0 to vertex 3 is (0 2 3)Shortest Path from vertex 1 to vertex 0 is (1 0)Shortest Path from vertex 1 to vertex 2 is (1 0 2)Shortest Path from vertex 1 to vertex 3 is (1 0 2 3)Shortest Path from vertex 2 to vertex 0 is (2 3 1 0)Shortest Path from vertex 2 to vertex 1 is (2 3 1)Shortest Path from vertex 2 to vertex 3 is (2 3)Shortest Path from vertex 3 to vertex 0 is (3 1 0)Shortest Path from vertex 3 to vertex 1 is (3 1)Shortest Path from vertex 3 to vertex 2 is (3 1 0 2)    

Floyd-Warshall算法的时间复杂度为O(V3),其中V是图中的顶点数。

约翰逊算法也可以用于在稀疏加权有向图中找到所有顶点对之间的最短路径。 它允许一些边缘权重为负数,但不存在负权重循环。 它使用贝尔曼 - 福特算法来转换输入图,从而从图中删除所有负权重,并在变换图上运行Dijkstra算法。

原创粉丝点击