图算法(3):Floyd-Warshall算法
2015-07-12 21:13
267 查看
Floyd-Warshall算法(英语:Floyd-Warshall algorithm),中文亦称弗洛伊德算法,是解决任意两点间的最短路径的一种算法,即全源最短路径问题(All-Pairs Shortest Paths Problem),可以正确处理有向图或负权的最短路径问题,同时也被用于计算有向图的传递闭包。
Floyd-Warshall算法的时间复杂度为O(N^3)[3],空间复杂度为O(N^2)。
解决单源最短路径问题的方案有 Dijkstra 算法和 Bellman-Ford 算法,对于全源最短路径问题可以认为是单源最短路径问题(Single Source Shortest Paths Problem)的推广,即分别以每个顶点作为源顶点并求其至其它顶点的最短距离。更通用的全源最短路径算法包括:
针对稠密图的 Floyd-Warshall 算法:时间复杂度为 O(V3);
针对稀疏图的 Johnson 算法:时间复杂度为 O(V2logV + VE);
原理:
Floyd-Warshall算法的原理是动态规划。
![](http://img.blog.csdn.net/20150712203206981)
最外层一维空间可以省略,因为D[k]只与Dk-1]有关。在实际算法中,为了节约空间,可以直接在原来空间上进行迭代,这样空间可降至二维。
算法描述:
其中dist[i][j]表示由点i到点j的代价,当其为 ∞ 表示两点之间没有任何连接。
注意:Floyd算法的本质是DP,而k是DP的阶段,因此要写最外面。
滚动数组优化:
![](http://img.blog.csdn.net/20150712203332851)
![](http://img.blog.csdn.net/20150712203344490)
上图是使用滚动数组,在第k阶段,计算d[i][j]时的情况。此时,由于使用d[][]这个二维数组作为滚动数组,在各个阶段的计算中被重复使用,因此数组中表示阶段的那一维也被取消了。在这图中,白色的格子,代表最新被计算过的元素(即第k阶段的新值),而灰色的格子中的元素值,其实保存的还是上一阶段(即第k-1阶段)的旧值。因此,在新的d[i][j]还未被计算出来时,d[i][j]中保存的值其实就对应之前没有用滚动数组时d[k-1][i][j]的值。此时,动态转移方程在隐藏掉阶段索引后就变为:
d[i][j] = min(d[i][j], d[i][k]+d[k][j])(k,i,j∈[1,n])
赋值号左侧d[i][j]就是我们要计算的第k阶段是i和j之间的最短路径长度。在这里,需要确保赋值号右侧的d[i][j], d[i][k]和d[k][j]的值是上一阶段(k-1阶段)的值。前面已经分析过了,在新的d[i][j]算出之前,d[i][j]元素保留的值的确就是上一阶段的旧值。但至于d[i][k]和d[k][j]呢?我们无法确定这两个元素是落在白色区域(新值)还是灰色区域(旧值)。好在有这样一条重要的性质,dp[k-1][i][k]和dp[k-1][k][j]是不会在第k阶段改变大小的。也就是说,凡是和k节点相连的边,在第k阶段的值都不会变。如何简单证明呢?我们可以把j=k代入之前的d[k][i][j]=min(d[k-1][i][j], d[k-1][i][k]+d[k-1][k][j])方程中,即:
d[k][i][k]
= min(d[k-1][i][k], d[k-1][i][k]+d[k-1][k][k])
= min(d[k-1][i][k], d[k-1][i][k]+0)
= d[k-1][i][k]
也就是说在第k-1阶段和第k阶段,点i和点k之间的最短路径长度是不变的。相同可以证明,在这两个阶段中,点k和点j之间的的最短路径长度也是不变的。因此,对于使用滚动数组的转移方程d[i][j] = min(d[i][j], d[i][k]+d[k][j])来说,赋值号右侧的d[i][j], d[i][k]和d[k][j]的值都是上一阶段(k-1阶段)的值,可以放心地被用来计算第k阶段时d[i][j]的值。
利用滚动数组改写后的Floyd算法代码如下:
参考:
https://zh.wikipedia.org/wiki/Floyd-Warshall%E7%AE%97%E6%B3%95
/article/4809993.html
http://www.wutianqi.com/?p=1903
http://tech.artyoo.me/?p=81
Floyd-Warshall算法的时间复杂度为O(N^3)[3],空间复杂度为O(N^2)。
解决单源最短路径问题的方案有 Dijkstra 算法和 Bellman-Ford 算法,对于全源最短路径问题可以认为是单源最短路径问题(Single Source Shortest Paths Problem)的推广,即分别以每个顶点作为源顶点并求其至其它顶点的最短距离。更通用的全源最短路径算法包括:
针对稠密图的 Floyd-Warshall 算法:时间复杂度为 O(V3);
针对稀疏图的 Johnson 算法:时间复杂度为 O(V2logV + VE);
原理:
Floyd-Warshall算法的原理是动态规划。
最外层一维空间可以省略,因为D[k]只与Dk-1]有关。在实际算法中,为了节约空间,可以直接在原来空间上进行迭代,这样空间可降至二维。
算法描述:
1 let dist be a |V| × |V| array of minimum distances initialized to ∞ (infinity) 2 for each vertex v 3 dist[v][v] ← 0 4 for each edge (u,v) 5 dist[u][v] ← w(u,v) // the weight of the edge (u,v) 6 for k from 1 to |V| 7 for i from 1 to |V| 8 for j from 1 to |V| 9 if dist[i][j] > dist[i][k] + dist[k][j] 10 dist[i][j] ← dist[i][k] + dist[k][j] 11 end if
其中dist[i][j]表示由点i到点j的代价,当其为 ∞ 表示两点之间没有任何连接。
注意:Floyd算法的本质是DP,而k是DP的阶段,因此要写最外面。
void floyd_original() { for(int i = 1; i <= n; i++) for(int j = 1; j <= n; j++) dist[0][i][j] = graph[i][j]; for(int k = 1; k <= n; k++) { for(int i = 1; i <= n; i++) { for(int j = 1; j <= n; j++) { dist[k][i][j] = min(dist[k-1][i][j], dist[k-1][i][k] + dist[k-1][k][j]); } } } }
滚动数组优化:
上图是使用滚动数组,在第k阶段,计算d[i][j]时的情况。此时,由于使用d[][]这个二维数组作为滚动数组,在各个阶段的计算中被重复使用,因此数组中表示阶段的那一维也被取消了。在这图中,白色的格子,代表最新被计算过的元素(即第k阶段的新值),而灰色的格子中的元素值,其实保存的还是上一阶段(即第k-1阶段)的旧值。因此,在新的d[i][j]还未被计算出来时,d[i][j]中保存的值其实就对应之前没有用滚动数组时d[k-1][i][j]的值。此时,动态转移方程在隐藏掉阶段索引后就变为:
d[i][j] = min(d[i][j], d[i][k]+d[k][j])(k,i,j∈[1,n])
赋值号左侧d[i][j]就是我们要计算的第k阶段是i和j之间的最短路径长度。在这里,需要确保赋值号右侧的d[i][j], d[i][k]和d[k][j]的值是上一阶段(k-1阶段)的值。前面已经分析过了,在新的d[i][j]算出之前,d[i][j]元素保留的值的确就是上一阶段的旧值。但至于d[i][k]和d[k][j]呢?我们无法确定这两个元素是落在白色区域(新值)还是灰色区域(旧值)。好在有这样一条重要的性质,dp[k-1][i][k]和dp[k-1][k][j]是不会在第k阶段改变大小的。也就是说,凡是和k节点相连的边,在第k阶段的值都不会变。如何简单证明呢?我们可以把j=k代入之前的d[k][i][j]=min(d[k-1][i][j], d[k-1][i][k]+d[k-1][k][j])方程中,即:
d[k][i][k]
= min(d[k-1][i][k], d[k-1][i][k]+d[k-1][k][k])
= min(d[k-1][i][k], d[k-1][i][k]+0)
= d[k-1][i][k]
也就是说在第k-1阶段和第k阶段,点i和点k之间的最短路径长度是不变的。相同可以证明,在这两个阶段中,点k和点j之间的的最短路径长度也是不变的。因此,对于使用滚动数组的转移方程d[i][j] = min(d[i][j], d[i][k]+d[k][j])来说,赋值号右侧的d[i][j], d[i][k]和d[k][j]的值都是上一阶段(k-1阶段)的值,可以放心地被用来计算第k阶段时d[i][j]的值。
利用滚动数组改写后的Floyd算法代码如下:
void floyd(){ int i,j,k; for(k=1;k<=n;k++) for(i=1;i<=n;i++) for(j=1;j<=n;j++) if(dist[i][k]+dist[k][j]<dist[i][j]) dist[i][j]=dist[i][k]+dist[k][j]; }
参考:
https://zh.wikipedia.org/wiki/Floyd-Warshall%E7%AE%97%E6%B3%95
/article/4809993.html
http://www.wutianqi.com/?p=1903
http://tech.artyoo.me/?p=81
相关文章推荐
- vector中元素为类对象时的一些使用方法
- C++ push方法与push_back方法 浅析
- linux shell 连接数据库的几种方法
- HDU-1016素数环问题
- px 与 dp, sp换算
- 【PHP教程一】在Mac下配置php开发环境:Apache+php+MySql
- 2014——我的求职(一个)
- 单例模式的C++实现
- iOS 9公测版来了 有什么变化?
- the classical examination test
- iOS 界面调试工具--Reveal
- 快速排序
- linux shell 连接数据库的几种方法
- nginx内核参数优化
- 【leetcode】Largest Rectangle in Histogram
- 为什么C++中拷贝构造函数的参数类型必须是引用?
- whu_选课
- web服务器监控工具
- 学习的路径
- leetcode刷题 ,总结,记录,备忘27