数据结构(十三):最短路径(Floyd算法)
Bellman-Ford
算法或者Dijkstra
算法用于解决单源最短路径问题,获取从指定起点出发,到达图中各个顶点的最短路径。若要获得图中每两个顶点之间的最短路径,则需要对算法执行
次,不过这里推荐另一种获得每对顶点间最短路径的方式。
Floyd-Warshall
算法使用动态规划策略计算图中每两个顶点间的最短路径,算法中通过调整路径中经过的中间顶点来缩小路径权值,最终得到每对顶点间的最短路径。
Floyd
算法允许图中存在负权边,但是不能存在负权回路。
算法介绍
对于图
,以
表示顶点集合,则从顶点
到顶点
的最短路径中经过的所有顶点都处于集合
中。对于顶点集合
,不妨以
表示从顶点
到顶点
的最短路径权值,且路径中经过的顶点都处于集合
中。因此当
时,因为此时的最短路径并非基于全部顶点集合,所以此时的
值可能要大于
。当
时,则有
,即此时的最短路径才是基于顶点集合
上真正的最短路径权值。
根据最短路径的特性,若从顶点
到顶点
的最短路径为
,则路径
为顶点
到顶点
的最短路径,因为若
之间存在权值更小的路径,则顶点
的最短路径
不成立,同理,路径
为顶点
到顶点
的最短路径。
对于顶点集合
,若顶点
到顶点
的最短路径中不包含顶点
,则有
;若最短路径中包含顶点
,则有
。若
,则
,表示顶点
和顶点
之间的路径上不存在其他顶点,路径权值即为边权值。
因此有如下递推关系式:
算法过程
根据该递推关系可知,对于任意两个顶点
,可以递增
值来逐渐获得最终的最短路径权值
。所以在算法的实现中,可以设置一个
二维矩阵,用于保存每两个顶点之间的路径权值,递增
值,遍历更新矩阵每个元素的路劲权值,当
时,此时矩阵中存储的则是任意顶点
之间的最短路径权值。
演示示例
graph
matrix
根据图 graph
构造矩阵 matrix
,其中顶点到自身的距离为 0,每两个顶点之间边的权值如 matrix
所示。
当
时,根据推导关系式
遍历更新矩阵元素,更新后,矩阵如下图所示:
matrix_1
随便元素值并没有发生变化,但此时对于任意顶点
,矩阵 matrix_1
中存储的都是
的值,表示任意两个顶点在中间顶点处于集合
上的最短路径权值。
递增
值,当
时,矩阵元素如下图所示:
matrix_8
此时矩阵 matrix_9
中存储的都是
的值,表示任意两个顶点在中间顶点处于集合
上的最短路径权值,即此时对于任意两个顶点有
。
算法示例
def floyd(graph):
matrix = graph.list
for k in range(graph.number):
for i in range(graph.number-1,-1,-1):
for j in range(graph.number):
if matrix[i][k] != None and matrix[k][j] != None and (matrix[i][j] == None or matrix[i][k] + matrix[k][j] < matrix[i][j]):
matrix[i][j] = matrix[i][k] + matrix[k][j]
floyd
算法较为简洁,代码中存在三层循环,第二层和第三层循环为遍历矩阵每个元素,根据递推关系式,更新每两个顶点之间的路径权值。第一层循环则是递增
值,直到
,此时更新矩阵元素,可以获得基于整个顶点集合上的最短路径权值。
性能分析
floyd
算法中存在三层循环,所以时间复杂度为
。
代码附录
from adjacencyMatrix import AdjacencyMatrix
# representing shortest path for each vertex
def floyd(graph):
matrix = graph.list
for k in range(graph.number):
for i in range(graph.number-1,-1,-1):
for j in range(graph.number):
if matrix[i][k] != None and matrix[k][j] != None and (matrix[i][j] == None or matrix[i][k] + matrix[k][j] < matrix[i][j]):
matrix[i][j] = matrix[i][k] + matrix[k][j]
def show(graph):
for i in range(graph.number):
print('node', (i + 1), 'distance:', end = ' ')
j = 0
while j < graph.number:
print(graph.list[i][j], end = ' ')
j += 1
print()
if __name__ == '__main__':
graph = AdjacencyMatrix(9)
graph.insert(1, 1, 0)
graph.insert(1, 2, 4)
graph.insert(1, 8, 8)
graph.insert(2, 1, 4)
graph.insert(2, 2, 0)
graph.insert(2, 3, 8)
graph.insert(2, 8, 11)
graph.insert(3, 2, 8)
graph.insert(3, 3, 0)
graph.insert(3, 4, 7)
graph.insert(3, 6, 4)
graph.insert(3, 9, 2)
graph.insert(4, 3, 7)
graph.insert(4, 4, 0)
graph.insert(4, 5, 9)
graph.insert(4, 6, 14)
graph.insert(5, 4, 9)
graph.insert(5, 5, 0)
graph.insert(5, 6, 10)
graph.insert(6, 3, 4)
graph.insert(6, 4, 14)
graph.insert(6, 5, 10)
graph.insert(6, 6, 0)
graph.insert(6, 7, 2)
graph.insert(7, 6, 2)
graph.insert(7, 7, 0)
graph.insert(7, 8, 1)
graph.insert(7, 9, 6)
graph.insert(8, 1, 8)
graph.insert(8, 2, 11)
graph.insert(8, 7, 1)
graph.insert(8, 8, 0)
graph.insert(8, 9, 7)
graph.insert(9, 3, 2)
graph.insert(9, 7, 6)
graph.insert(9, 8, 7)
graph.insert(9, 9, 0)
floyd(graph)
show(graph)
- 两个目录中,删除其中一个目录中同名文件的做法
- linux下监控某个目录是否被更改
- centos下升级git版本的操作记录
- linux下core file size设置笔记
- linux下文件加密操作记录
- python的with语句,超级强大
- “AS3.0高级动画编程”学习:第二章转向行为(上)
- Linux下性能调试工具-top和sar运维笔记
- Apache+wsgi+flask部署
- “勒索病毒”到底会勒索啥,尽可以做到让全球对之恐惧无奈!
- 解决win10 关键错误开始菜单和cortana无法工作 的问题(转-真的成功了)
- “AS3.0高级动画编程”学习:第二章转向行为(下)
- windows系统中eclipse C开发环境的架设
- 5个酷毙的Python工具
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- zabbix 监控项
- [PyQt Tutorial]4.使用Qt Designer
- [PyQt Tutorial]5.Signals & Slots(信号与槽)
- Docker数据共享与持久化
- [PyQt Tutorial]6.Layout Management(布局管理)
- Kubernetes入门
- [PyQt Tutorial]7.QDialog 类
- kubeadm快速部署kubernetes集群
- [PyQt Tutorial]8.QMessageBox
- Tomcat_01_简介
- Tomcat调优
- Tomcat_02_应用部署
- Tomcat_03_监控
- Kubernetes常用命令
- Oracle限制用户和客户端登陆