ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

最短路径算法之——Floyd算法介绍与实现

2022-08-03 23:05:25  阅读:181  来源: 互联网

标签:int vertex vertices 最短 midVertex 算法 Floyd path 顶点


之前我们学习了图的最短路径算法之Dijkstra算法,知道此算法是用来求指定的两顶点间最短路径的(也称单源最短路径single-source),如果要求图中任意两顶点间的最短路径,怎么办呢?

当然可以通过对任意两点调用Dijkstra算法来实现。有没有更好的办法呢?

这里我们介绍下Floyd算法(Floyd–Warshall、Roy–Warshall、Roy–Floyd都指同一种算法),就是专门用来求解此问题的。

我们先看下该算法的基本思想。如图示,我们有三个顶点 a b c

顶点a到c的直达路径为a-c,距离为4。为了探索其它更短路径,我们选择一个新的顶点b,看b是否能缩短a-c的距离,得知a-b距离为1,b-c距离为2, 1+2<4,说明经过b点是可以缩短距离的,即a-c的更短的距离等于a-b距离加上b-c的距离。

可以看到通过引入新的顶点,达到了缩短原有路程的目的。新的顶点我们可以理解为排除开始和结束两顶点,其他顶点的统称,通过依次引入这些顶点,判断开始点到此顶点距离加上此顶点到结束点的距离,如果小于开始和顶点间的原始距离,则更新为这个距离。

接下来我们就按照这个思路来编写代码。演示图顶点信息如下

代码

  1 public class Floyd {
  2     private static final int MAX_WEIGHT = 999;
  3     private static final int NOT_EXISTS_PATH = -1;
  4 
  5     public static void main(String[] args) {
  6         //顶点个数
  7         int vertexNum = 4;
  8         //顶点数据
  9         int[][] vertices = initVertex(vertexNum);
 10         //路径默认数据
 11         int[][] path = initPath(vertices);
 12         print(vertices, "vertex");
 13         print(path, "path");
 14 
 15         //midVertex为中间顶点,判断加入此顶点后能否缩短原有距离。例如a至b的距离,则转换为a至midVertex距离加上midVertex至b的距离。
 16         //每个顶点都需要做一次中间顶点,所以最外层为中间顶点
 17         for (int midVertex = 0; midVertex < vertexNum; midVertex++) {
 18             //顶点要两两判断,这样才能求解任意两个顶点的距离,因此是一个两层循环
 19             for (int i = 0; i < vertexNum; i++) {
 20                 for (int j = 0; j < vertexNum; j++) {
 21                     //i到j距离,对比i到midVertex距离加上midVertex到j的距离,小于则更新为这个midVertex顶点
 22                     if (vertices[i][j] > vertices[i][midVertex] + vertices[midVertex][j]) {
 23                         System.out.printf("midVertex %d %d-%d dist=%d>%d+%d ", midVertex, i, j, vertices[i][j], vertices[i][midVertex], vertices[midVertex][j]);
 24                         vertices[i][j] = vertices[i][midVertex] + vertices[midVertex][j];
 25                         System.out.printf("path=%d\n", path[midVertex][j]);
 26                         //注意!直接前驱顶点不能直接设置为midVertex,而是取midVertex到结束顶点j的直接前驱顶点
 27                         path[i][j] = path[midVertex][j];
 28                     }
 29                 }
 30             }
 31         }
 32 
 33         print(vertices, "vertex");
 34         print(path, "path");
 35         //顶点3到2的最短路径经过的各个边
 36         printShortestPath(3, 2, path);
 37     }
 38 
 39     private static void printShortestPath(int i, int j, int[][] path) {
 40         System.out.printf("[path %d-%d]\n", i, j);
 41         while (path[i][j] != NOT_EXISTS_PATH) {
 42             System.out.printf("%d->%d\n", path[i][j], j);
 43             j = path[i][j];
 44         }
 45     }
 46 
 47     private static int[][] initVertex(int vertexNum) {
 48         int[][] vertex = new int[vertexNum][vertexNum];
 49         for (int i = 0; i < vertexNum; i++) {
 50             for (int j = 0; j < vertexNum; j++) {
 51                 if (i == j) {
 52                     vertex[i][i] = 0;
 53                 } else {
 54                     vertex[i][j] = MAX_WEIGHT;
 55                 }
 56             }
 57         }
 58         vertex[0][1] = 2;
 59         vertex[0][2] = 6;
 60         vertex[0][3] = 4;
 61 
 62         vertex[1][2] = 3;
 63 
 64         vertex[2][0] = 7;
 65         vertex[2][3] = 1;
 66 
 67         vertex[3][0] = 5;
 68         vertex[3][2] = 12;
 69         return vertex;
 70     }
 71 
 72     private static int[][] initPath(int[][] vertices) {
 73         int[][] path = new int[vertices.length][vertices.length];
 74         for (int i = 0; i < vertices.length; i++) {
 75             for (int j = 0; j < vertices.length; j++) {
 76                 if (vertices[i][j] != MAX_WEIGHT && i != j) {
 77                     path[i][j] = i;
 78                 } else {
 79                     path[i][j] = NOT_EXISTS_PATH;
 80                 }
 81             }
 82         }
 83         return path;
 84     }
 85 
 86     private static void print(int[][] vertices, String name) {
 87         System.out.printf("[%s]\n", name);
 88         System.out.print("+   ");
 89         for (int i = 0; i < vertices.length; i++) {
 90             System.out.printf("%d   ", i);
 91         }
 92         System.out.println();
 93         int index = 0;
 94         for (int[] ints : vertices) {
 95             System.out.printf("%d  ", index++);
 96             for (int j = 0; j < vertices.length; j++) {
 97                 if (ints[j] == MAX_WEIGHT) {
 98                     System.out.print(" -  ");
 99                 } else {
100                     System.out.printf("%2d  ", ints[j]);
101                 }
102             }
103             System.out.println();
104         }
105     }
106 }

输出

[vertex] #顶点信息,第一行、第一列都为下标数据方便数据查看。“-”为无穷大(内部用的999表示),可以认为是两个顶点间无直接路径
+   0   1   2   3   
0   0   2   6   4  
1   -   0   3   -  
2   7   -   0   1  
3   5   -  12   0  
[path]  #默认的各顶点直接前驱的顶点, -1为此路径不存在,即不存在前驱
+   0   1   2   3   
0  -1   0   0   0  
1  -1  -1   1  -1  
2   2  -1  -1   2  
3   3  -1   3  -1  
midVertex 0 2-1 dist=999>7+2 path=0  #以顶点0为中间顶点,判断0的引入是否能缩短2-1间的路径,因为2-1没有直接路径相连,默认为无穷大,2-0长度为7,0-1长度为2,7+2小于无穷大(999),因此把2-1路径长度改为9,同时把顶点1的前驱改为顶点0
midVertex 0 3-1 dist=999>5+2 path=0  #同上判断3-0,0-1,相加后为7,小于无穷大,更新,同时把顶点1的直接前驱改为顶点0
midVertex 0 3-2 dist=12>5+6 path=0
midVertex 1 0-2 dist=6>2+3 path=1
midVertex 1 3-2 dist=11>7+3 path=1
midVertex 2 1-0 dist=999>3+7 path=2
midVertex 2 1-3 dist=999>3+1 path=2
midVertex 3 1-0 dist=10>4+5 path=3
midVertex 3 2-0 dist=7>1+5 path=3
midVertex 3 2-1 dist=9>1+7 path=0  #顶点1的直接前驱顶点改为0,注意这个0是中间顶点3-1对应的直接前驱顶点0
[vertex]  #最终各个顶点间的最短路径
+   0   1   2   3   
0   0   2   5   4  
1   9   0   3   4  
2   6   8   0   1  
3   5   7  10   0  
[path]  #各顶点的直接前驱顶点
+   0   1   2   3   
0  -1   0   1   0  
1   3  -1   1   2  
2   3   0  -1   2  
3   3   0   1  -1  
[path 3-2]  #顶点3到2最短路径经过的边
1->2
0->1
3->0

可以看到Floyd算法的代码是相当简练的:最外层为要引入的中间顶点,即判断这个顶点的加入是否能缩短任意两个顶点间的距离,而任意一个顶点都可以当做中间顶点,再加上任意两个顶点的组合(两层循环),就构成了一个三层的循环。因此随着顶点数量的增多,循环的次数增长较快(假设顶点个数为N,则对应的循环次数为N*N*N=N3)。


参考资料
https://baike.baidu.com/item/Floyd%E7%AE%97%E6%B3%95/291990

https://brilliant.org/wiki/floyd-warshall-algorithm/

https://www.bilibili.com/video/BV1K5411f7G7

https://www.bilibili.com/video/BV1RK4y1d7ct

 

标签:int,vertex,vertices,最短,midVertex,算法,Floyd,path,顶点
来源: https://www.cnblogs.com/binary220615/p/16548795.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有