图论动态规划算法——Floyd最短路径

前言

推出一个新系列,《看图轻松理解数据结构和算法》,主要使用图片来描述常见的数据结构和算法,轻松阅读并理解掌握。本系列包括各类堆、各类队列、各类列表、各类树、各类图、各类排序等等几十篇的样子。mysql

Floyd算法

Floyd是一种经典的多源最短路径算法,它经过动态规划的思想来寻找给定加权图中的多源点之间的最短路径,算法时间复杂度是O(n3)。之因此叫Floyd是由于该算法发明人之一是Robert Floyd,他是1978年图灵奖得到者,同时也是斯坦福大学计算机科学系教授。算法

image

核心思想

对于n个点的加权图,从加权图的权值矩阵开始,递归地更新n次最终求得每两点之间的最短路径矩阵。即加权图的邻接矩阵为D=(d_{ij})_{n \times n},按必定公式对该矩阵进行递归更新,初始时矩阵为D(0),第一次,根据公式用D(0)构建出矩阵D(1);第二次,根据公式用D(1)构建出矩阵D(2);以此类推,根据公式用D(n-1)构造出矩阵D(n),D(n)即为图的距离矩阵,i行j列表示顶点i到顶点j的最短距离。sql

动态规划

如何理解这种动态规划算法呢?能够理解为逐一选择中转点,而后针对该中转点,全部以此为中转点的其它点都要根据规定进行更新,这个规定就是原来两点之间的距离若是经过该中转点变小了则更新距离矩阵。好比选择“1”做为中转点,原来“02”之间的距离为5,经过中转点1后(即路径变为“012”)距离为4,变小了,那么就更新距离矩阵对应的元素,由5更新为4。当图中全部顶点都被做为中转点处理之后,那么获得的最后距离矩阵就是多源最短距离矩阵了。数组

c(i,j,k)为i到j的中间节点编号不超过k的最短距离,当k=0时,c(i,j,0)=d_{ij},对于n个顶点的图,咱们要求的i到j的最短距离便是c(i,j,n-1)bash

如今咱们创建c(i,j,k)c(i,j,k-1)之间的递归关系,对于任意k,c(i,j,k)=min(c(i,j,k-1),c(i,k,k-1)+c(k,j,k-1)),因而能够根据该递归关系获得最终的最短路径,即中间节点编号不超过n-1的最短距离。网络

算法过程

  1. 从任意一条单边路径开始,全部两点之间的距离是边的权重,若是两点之间没有边相连,则权重为无穷大。即用数组D=(d_{ij})_{n \times n}来记录i,j之间的最短距离,初始时,若i=j则dis[i][j]=0。若i、j两点之间相连则dis[i][j]的值为该边的权值。若i、j两点没有相连则dis[i][j]的值为无穷。
  2. 对于每一对顶点u和v,看看是否存在一个顶点w使得从u到w再到v比已知的路径更短,若是存在则更新。即对全部的k值(1<k<n),计算(d[i][k]+d[k][j])的值,若其小于d[i][j],则d[i][j]=d[i][k]+d[k][j],不然d[i][j]的值不变。

神奇的五行代码

Floyd算法核心就是下列五行代码,能够体会一下,三个for循环嵌套,最外层的k便是循环取不一样中转点时的状况,分别让图中每一个顶点做为中转点去更新距离,完成全部循环后就是最终的最短距离矩阵。数据结构

for(k=1;k<=n;k++)
    for(i=1;i<=n;i++)
        for(j=1;j<=n;j++) 
            if(d[i][k]+d[k][j]<d[i][j]) 
                    d[i][j]=d[i][k]+d[k][j];
复制代码

优缺点

优势:容易理解,能够算出任意两个节点之间的最短距离,代码编写简单。对于稠密图效果最佳,边权可正可负。并发

缺点:时间复杂度比较高,不适合计算大量数据。机器学习

执行过程

对于一个拥有7个顶点的无向加权图,分别用0-6来表示图的每一个顶点,每条边上的数字为对应的权重。数据结构和算法

image

首先根据每条边的权重来初始化距离矩阵,这是一个[7x7]的矩阵,行列分别对应两个顶点,好比第0行第1列表示顶点0到顶点1,对应的值为3,即权值为3。以此类推,其它元素分别表明对应边的权重。

image

当以顶点0为中转点时

逐一查找看是否有以0为中转点使得距离更短,实际上并不用比较全部矩阵的元素,只需比较知足if (i != j && j != k && i != k)条件的元素,即都为0的对角线和中转点对应的行列不用比较,由于对角线上的元素表示顶点本身到本身的距离,因此无需比较,而中转点对应的行列表示顶点到中转点的距离,也无需比较。

比较d[1][2]d[1][0]+d[0][2],由于1<3+5,因此不更新d[1][2]

image

往下,比较d[1][3]d[1][0]+d[0][3],由于4<3+INF,因此不更新d[1][3]

image

往下,比较d[1][4]d[1][0]+d[0][4],由于INF<3+INF,因此不更新d[1][4]。接着往下d[1][5]d[1][6]两个元素状况相似。

image

比较d[2][1]d[2][0]+d[0][1],由于1<3+5,因此不更新d[2][1]

image

往下,比较d[2][3]d[2][0]+d[0][3],由于4<5+INF,因此不更新d[2][3]

image

往下,比较d[2][4]d[2][0]+d[0][4],由于8<5+INF,因此不更新d[2][4]

image

往下,比较d[2][5]d[2][0]+d[0][5],由于2<5+INF,因此不更新d[2][5]

image

往下,比较d[2][6]d[2][0]+d[0][6],由于INF<5+INF,因此不更新d[2][6]

image

比较d[3][1]d[3][0]+d[0][1],由于4<INF+3,因此不更新d[3][1]

image

相似地,剩下的元素所有都不更新,最后比较完d[6][5]d[6][0]+d[0][5]后即完成了以0做为中转点的所有比较工做。

image

当以顶点1为中转点时

逐一查找看是否有以1为中转点使得距离更短,比较d[0][2]d[0][1]+d[1][2]

image

由于5>3+1,因此将d[0][2]的值更新为4。

image

比较d[0][3]d[0][1]+d[1][3]

image

由于INF>3+4,因此将d[0][3]的值更新为7。

image

第0行接着的三个元素都不更新,到第2行后,比较d[2][0]d[2][1]+d[1][0]

image

由于5>1+3,因此将d[2][0]的值更新为4。第二行剩余的元素都无需更新。

image

开始第3行,比较d[3][0]d[3][1]+d[1][0]

image

由于INF>4+3,因此将d[3][0]的值更新为7。

image

接着以顶点1做为中转点时剩余的所有元素都无需更新。

当以顶点2为中转点时

逐一查找看是否有以2为中转点使得距离更短,比较d[0][1]d[0][2]+d[2][1],由于3<4+1,因此d[0][1]不更新。

image

比较d[0][3]d[0][2]+d[2][3],由于7<4+4,因此d[0][3]不更新。

image

比较d[0][4]d[0][2]+d[2][4]

image

由于INF>4+8,因此将d[0][4]的值更新为12。

image

相似地,对以顶点2做为中转点的所有剩余元素进行比较更新。

当以顶点3为中转点时

逐一查找看是否有以3为中转点使得距离更短,比较d[0][1]d[0][3]+d[3][1],由于3<7+4,因此d[0][1]不更新。

image

比较d[0][2]d[0][3]+d[3][2],由于4<7+4,因此d[0][2]不更新。

image

相似地,对以顶点3做为中转点的所有剩余元素进行比较更新。

当以顶点4为中转点时

逐一查找看是否有以4为中转点使得距离更短,比较d[0][1]d[0][4]+d[4][1],由于3<12+9,因此d[0][1]不更新。

image

比较d[0][2]d[0][4]+d[4][2],由于4<12+8,因此d[0][2]不更新。

image

相似地,对以顶点4做为中转点的所有剩余元素进行比较更新。

当以顶点5为中转点时

逐一查找看是否有以5为中转点使得距离更短,比较d[0][1]d[0][5]+d[5][1],由于3<6+3,因此d[0][1]不更新。

image

比较d[0][2]d[0][5]+d[5][2],由于4<6+2,因此d[0][2]不更新。

image

相似地,对以顶点5做为中转点的所有剩余元素进行比较更新。

当以顶点6为中转点时

逐一查找看是否有以6为中转点使得距离更短,比较d[0][1]d[0][6]+d[6][1],由于3<9+6,因此d[0][1]不更新。

image

比较d[0][2]d[0][6]+d[56][2],由于4<9+5,因此d[0][2]不更新。

image

相似地,对以顶点6做为中转点的所有剩余元素进行比较更新。

最终矩阵

将全部顶点做为中转点处理后,最终获得了一个矩阵,这个矩阵就是图的每一个顶点两两最短距离矩阵。好比a[0][4]=12就是顶点0到顶点4的最短距离为12。同时能够看到距离矩阵是以对角线为轴对称的。

image

-------------推荐阅读------------

个人开源项目汇总(机器&深度学习、NLP、网络IO、AIML、mysql协议、chatbot)

为何写《Tomcat内核设计剖析》

个人2017文章汇总——机器学习篇

个人2017文章汇总——Java及中间件

个人2017文章汇总——深度学习篇

个人2017文章汇总——JDK源码篇

个人2017文章汇总——天然语言处理篇

个人2017文章汇总——Java并发篇


跟我交流,向我提问:

欢迎关注:

相关文章
相关标签/搜索