Floyd-Warshall 全源最短路径算法
Floyd-Warshall 算法采用动态规划方案来解决在一个有向图 G = (V, E) 上每对顶点间的最短路径问题,即全源最短路径问题(All-Pairs Shortest Paths Problem),其中图 G 允许存在权值为负的边,但不存在权值为负的回路。Floyd-Warshall 算法的运行时间为 Θ(V3)。
Floyd-Warshall 算法由 Robert Floyd 于 1962 年提出,但其实质上与 Bernad Roy 于 1959 年和 Stephen Warshall 于 1962 年提出的算法相同。
解决单源最短路径问题的方案有 Dijkstra 算法和 Bellman-Ford 算法,对于全源最短路径问题可以认为是单源最短路径问题(Single Source Shortest Paths Problem)的推广,即分别以每个顶点作为源顶点并求其至其它顶点的最短距离。更通用的全源最短路径算法包括:
- 针对稠密图的 Floyd-Warshall 算法:时间复杂度为 O(V3);
- 针对稀疏图的 Johnson 算法:时间复杂度为 O(V2logV + VE);
最短路径算法中的最优子结构指的是两顶点之间的最短路径包括路径上其它顶点的最短路径。具体描述为:对于给定的带权图 G = (V, E),设 p = <v1, v2, …,vk> 是从 v1 到 vk 的最短路径,那么对于任意 i 和 j,1 ≤ i ≤ j ≤ k,pij = <vi, vi+1, …, vj> 为 p 中顶点 vi 到 vj 的子路径,那么 pij 是顶点 vi 到 vj 的最短路径。
Floyd-Warshall 算法的设计基于了如下观察。设带权图 G = (V, E) 中的所有顶点 V = {1, 2, . . . , n},考虑一个顶点子集 {1, 2, . . . , k}。对于任意对顶点 i, j,考虑从顶点 i 到 j 的所有路径的中间顶点都来自该子集 {1, 2, . . . , k},设 p 是该子集中的最短路径。Floyd-Warshall 算法描述了 p 与 i, j 间最短路径及中间顶点集合 {1, 2, . . . , k - 1} 的关系,该关系依赖于 k 是否是路径 p 上的一个中间顶点。
算法伪码如下:
最短路径算法的设计都使用了松弛(relaxation)技术。在算法开始时只知道图中边的权值,然后随着处理逐渐得到各对顶点的最短路径的信息,算法会逐渐更新这些信息,每步都会检查是否可以找到一条路径比当前已有路径更短,这一过程通常称为松弛(relaxation)。
C# 代码实现:
using System;
using System.Collections.Generic;
using System.Linq; namespace GraphAlgorithmTesting
{
class Program
{
static void Main(string[] args)
{
int[,] graph = new int[, ]
{
{, , , , , , , , },
{, , , , , , , , },
{, , , , , , , , },
{, , , , , , , , },
{, , , , , , , , },
{, , , , , , , , },
{, , , , , , , , },
{, , , , , , , , },
{, , , , , , , , }
}; Graph g = new Graph(graph.GetLength());
for (int i = ; i < graph.GetLength(); i++)
{
for (int j = ; j < graph.GetLength(); j++)
{
if (graph[i, j] > )
g.AddEdge(i, j, graph[i, j]);
}
} Console.WriteLine("Graph Vertex Count : {0}", g.VertexCount);
Console.WriteLine("Graph Edge Count : {0}", g.EdgeCount);
Console.WriteLine(); int[,] distSet = g.FloydWarshell();
PrintSolution(g, distSet); // build a directed and negative weighted graph
Graph directedGraph1 = new Graph();
directedGraph1.AddEdge(, , -);
directedGraph1.AddEdge(, , );
directedGraph1.AddEdge(, , );
directedGraph1.AddEdge(, , );
directedGraph1.AddEdge(, , );
directedGraph1.AddEdge(, , );
directedGraph1.AddEdge(, , );
directedGraph1.AddEdge(, , -); Console.WriteLine();
Console.WriteLine("Graph Vertex Count : {0}", directedGraph1.VertexCount);
Console.WriteLine("Graph Edge Count : {0}", directedGraph1.EdgeCount);
Console.WriteLine(); int[,] distSet1 = directedGraph1.FloydWarshell();
PrintSolution(directedGraph1, distSet1); // build a directed and positive weighted graph
Graph directedGraph2 = new Graph();
directedGraph2.AddEdge(, , );
directedGraph2.AddEdge(, , );
directedGraph2.AddEdge(, , );
directedGraph2.AddEdge(, , ); Console.WriteLine();
Console.WriteLine("Graph Vertex Count : {0}", directedGraph2.VertexCount);
Console.WriteLine("Graph Edge Count : {0}", directedGraph2.EdgeCount);
Console.WriteLine(); int[,] distSet2 = directedGraph2.FloydWarshell();
PrintSolution(directedGraph2, distSet2); Console.ReadKey();
} private static void PrintSolution(Graph g, int[,] distSet)
{
Console.Write("\t");
for (int i = ; i < g.VertexCount; i++)
{
Console.Write(i + "\t");
}
Console.WriteLine();
Console.Write("\t");
for (int i = ; i < g.VertexCount; i++)
{
Console.Write("-" + "\t");
}
Console.WriteLine();
for (int i = ; i < g.VertexCount; i++)
{
Console.Write(i + "|\t");
for (int j = ; j < g.VertexCount; j++)
{
if (distSet[i, j] == int.MaxValue)
{
Console.Write("INF" + "\t");
}
else
{
Console.Write(distSet[i, j] + "\t");
}
}
Console.WriteLine();
}
} class Edge
{
public Edge(int begin, int end, int weight)
{
this.Begin = begin;
this.End = end;
this.Weight = weight;
} public int Begin { get; private set; }
public int End { get; private set; }
public int Weight { get; private set; } public override string ToString()
{
return string.Format(
"Begin[{0}], End[{1}], Weight[{2}]",
Begin, End, Weight);
}
} class Graph
{
private Dictionary<int, List<Edge>> _adjacentEdges
= new Dictionary<int, List<Edge>>(); public Graph(int vertexCount)
{
this.VertexCount = vertexCount;
} public int VertexCount { get; private set; } public int EdgeCount
{
get
{
return _adjacentEdges.Values.SelectMany(e => e).Count();
}
} public void AddEdge(int begin, int end, int weight)
{
if (!_adjacentEdges.ContainsKey(begin))
{
var edges = new List<Edge>();
_adjacentEdges.Add(begin, edges);
} _adjacentEdges[begin].Add(new Edge(begin, end, weight));
} public int[,] FloydWarshell()
{
/* distSet[,] will be the output matrix that will finally have the shortest
distances between every pair of vertices */
int[,] distSet = new int[VertexCount, VertexCount]; for (int i = ; i < VertexCount; i++)
{
for (int j = ; j < VertexCount; j++)
{
distSet[i, j] = int.MaxValue;
}
}
for (int i = ; i < VertexCount; i++)
{
distSet[i, i] = ;
} /* Initialize the solution matrix same as input graph matrix. Or
we can say the initial values of shortest distances are based
on shortest paths considering no intermediate vertex. */
foreach (var edge in _adjacentEdges.Values.SelectMany(e => e))
{
distSet[edge.Begin, edge.End] = edge.Weight;
} /* Add all vertices one by one to the set of intermediate vertices.
---> Before start of a iteration, we have shortest distances between all
pairs of vertices such that the shortest distances consider only the
vertices in set {0, 1, 2, .. k-1} as intermediate vertices.
---> After the end of a iteration, vertex no. k is added to the set of
intermediate vertices and the set becomes {0, 1, 2, .. k} */
for (int k = ; k < VertexCount; k++)
{
// Pick all vertices as source one by one
for (int i = ; i < VertexCount; i++)
{
// Pick all vertices as destination for the above picked source
for (int j = ; j < VertexCount; j++)
{
// If vertex k is on the shortest path from
// i to j, then update the value of distSet[i,j]
if (distSet[i, k] != int.MaxValue
&& distSet[k, j] != int.MaxValue
&& distSet[i, k] + distSet[k, j] < distSet[i, j])
{
distSet[i, j] = distSet[i, k] + distSet[k, j];
}
}
}
} return distSet;
}
}
}
}
运行结果如下:
参考资料
- 广度优先搜索
- 深度优先搜索
- Breadth First Traversal for a Graph
- Depth First Traversal for a Graph
- Dijkstra 单源最短路径算法
- Bellman-Ford 单源最短路径算法
- Bellman–Ford algorithm
- Introduction To Algorithm
- Floyd-Warshall's algorithm
- Bellman-Ford algorithm for single-source shortest paths
- Dynamic Programming | Set 23 (Bellman–Ford Algorithm)
- Dynamic Programming | Set 16 (Floyd Warshall Algorithm)
- Johnson’s algorithm for All-pairs shortest paths
- Floyd-Warshall's algorithm
- 最短路径算法--Dijkstra算法,Bellmanford算法,Floyd算法,Johnson算法
- QuickGraph, Graph Data Structures And Algorithms for .NET
- CHAPTER 26: ALL-PAIRS SHORTEST PATHS
本篇文章《Floyd-Warshall 全源最短路径算法》由 Dennis Gao 发表自博客园,未经作者本人同意禁止任何形式的转载,任何自动或人为的爬虫转载行为均为耍流氓。
Floyd-Warshall 全源最短路径算法的更多相关文章
- Johnson 全源最短路径算法
解决单源最短路径问题(Single Source Shortest Paths Problem)的算法包括: Dijkstra 单源最短路径算法:时间复杂度为 O(E + VlogV),要求权值非负: ...
- Johnson 全源最短路径算法学习笔记
Johnson 全源最短路径算法学习笔记 如果你希望得到带互动的极简文字体验,请点这里 我们来学习johnson Johnson 算法是一种在边加权有向图中找到所有顶点对之间最短路径的方法.它允许一些 ...
- 经典贪心算法(哈夫曼算法,Dijstra单源最短路径算法,最小费用最大流)
哈夫曼编码与哈夫曼算法 哈弗曼编码的目的是,如何用更短的bit来编码数据. 通过变长编码压缩编码长度.我们知道普通的编码都是定长的,比如常用的ASCII编码,每个字符都是8个bit.但在很多情况下,数 ...
- Dijkstra 单源最短路径算法
Dijkstra 算法是一种用于计算带权有向图中单源最短路径(SSSP:Single-Source Shortest Path)的算法,由计算机科学家 Edsger Dijkstra 于 1956 年 ...
- Bellman-Ford 单源最短路径算法
Bellman-Ford 算法是一种用于计算带权有向图中单源最短路径(SSSP:Single-Source Shortest Path)的算法.该算法由 Richard Bellman 和 Leste ...
- 单源最短路径算法:迪杰斯特拉 (Dijkstra) 算法(二)
一.基于邻接表的Dijkstra算法 如前一篇文章所述,在 Dijkstra 的算法中,维护了两组,一组包含已经包含在最短路径树中的顶点列表,另一组包含尚未包含的顶点.使用邻接表表示,可以使用 BFS ...
- 单源最短路径算法:迪杰斯特拉 (Dijkstra) 算法(一)
一.算法介绍 迪杰斯特拉算法(英语:Dijkstra's algorithm)由荷兰计算机科学家艾兹赫尔·迪杰斯特拉在1956年提出.迪杰斯特拉算法使用了广度优先搜索解决赋权有向图的单源最短路径问题. ...
- 多源最短路径算法—Floyd算法
前言 在图论中,在寻路最短路径中除了Dijkstra算法以外,还有Floyd算法也是非常经典,然而两种算法还是有区别的,Floyd主要计算多源最短路径. 在单源正权值最短路径,我们会用Dijkstra ...
- 多源最短路径算法:Floyd算法
前言 由于本人太菜,这里不讨论Floyd的正确性. 简介 多源最短路径,解决的是求从图中任意两点之间的最短路径的问题. 分析 代码短小精悍,主要代码只有四行,直接放上: for(int k=1;k&l ...
随机推荐
- 通过Jexus 部署 dotnetcore版本MusicStore 示例程序
ASPNET Music Store application 是一个展示最新的.NET 平台(包括.NET Core/Mono等)上使用MVC 和Entity Framework的示例程序,本文将展示 ...
- .Net多线程编程—预备知识
1 基本概念 共享内存的多核架构:一个单独的封装包内封装了多个互相连接的未处理器,且所有内核都可以访问主内存.共享内存的多核系统的一些微架构,例如内核暂停功能,超频. 内核暂停功能:当使用内核不多的时 ...
- ExtJS 4.2 Date组件扩展:添加清除按钮
ExtJS中除了提供丰富的组件外,我们还可以扩展他的组件. 在这里,我们将在Date日期组件上添加一个[清除]按钮,用于此组件已选中值的清除. 目录 1. Date组件介绍 2. 主要代码说明 3. ...
- tomcat开发远程调试端口以及利用eclipse进行远程调试
一.tomcat开发远程调试端口 方法1 WIN系统 在catalina.bat里: SET CATALINA_OPTS=-server -Xdebug -Xnoagent -Djava.compi ...
- iOS开发之多种Cell高度自适应实现方案的UI流畅度分析
本篇博客的主题是关于UI操作流畅度优化的一篇博客,我们以TableView中填充多个根据内容自适应高度的Cell来作为本篇博客的使用场景.当然Cell高度的自适应网上的解决方案是铺天盖地呢,今天我们的 ...
- python与c互相调用
虽然python开发效率很高,但作为脚本语言,其性能不高,所以为了兼顾开发效率和性能,通常把性能要求高的模块用c或c++来实现或者在c或c++中运行python脚本来处理逻辑,前者通常是python中 ...
- Java虚拟机 JVM
finalize();(不建议使用,代价高,不确定性大) 如果你在一个类中覆写了finalize()方法, 那么你可以在第一次被GC的时候,挽救一个你想挽救的对象,让其不被回收,但只能挽救一次. GC ...
- windows 2012 r2 can't find kb2919355
问题 解决: 1.手动安装了 Windows8.1-KB2919442-x64 2.手动下载 KB2919355 更新成功 Turns out to have been a result ...
- 元素绝对居中终极办法兼容IE8
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...
- Java模拟Windows的Event
场景 开发中遇到一个场景,业务操作会不定时的产生工作任务,这些工作任务需要放入到一个队列中,而另外会有一个线程一直检测这个队列,队列中有任务就从队列中取出并进行运算. 问题 业务场景倒是简单,只不过这 ...