最短路径
非网图:它没有边上的权值,所谓的最短路径,其实就是指两顶点之间经过的边数最少的路径;网图:是指两顶点之间经过的边上权值之和最少的路径,并且我们称路径上的第一个顶点是源点,最后一个顶点是终点。
迪杰斯特拉算法(Dijkstra)
典型最短路径算法,用于计算一个节点到其他节点的最短路径。主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止。 按路径递增的顺序产生最短路径的算法。
问题描述
在无向图 G=(V,E) 中,假设每条边E[i]的长度为w[i],找到由顶点V0到其余各点的最短路径。
思路
-
初始时,S只包含源点,即S={v},v的距离为0。U包含除v外的其他顶点,若v与U中顶点u有边,则<u,v>有权值,否则<u,v>权值为∞。
-
从U中选取一个距离v最小的顶点k,把k加入S中(该选定的距离就是v到k的最短路径长度)。
-
以顶点k为中心,修改U中各顶点的距离;若从源点v到顶点u的距离(经过顶点k)比原来距离(不经过顶点k)短,则修改顶点u的距离值,修改后的距离值=v到k的最短路径 + v到k的路径。
-
重复步骤b和c直到所有顶点都包含在S中。
代码中使用finalPath的值来区分是在集合S中还是集合U中
- finalPath把顶点分为两类,finalpath[i] = 0 表示未获取到最短路径,finalPath[i] = 1表示已经获取到最短路径。
- pathSrc[i] 存储顶点i的前驱顶点。
- shortPath[i]存储V0到顶点i的最短路径。
实现
1static int[] pathSrc = new int[9];
2static int[] shortPath = new int[9];
3static void shortestPath_DijkStra(MGraph m, int v0) {
4 // finalPath[w] = 1 表示已经获取到顶点V0到Vw的最短路径
5 int[] finalPath = new int[9];
6 for (int i = 0; i < m.numVertexes; i++) {
7 finalPath[i] = 0;
8 shortPath[i] = m.arc[v0][i];
9 pathSrc[i] = 0;
10 }
11 // v0到v0的路径为0
12 shortPath[v0] = 0;
13 // vo到v0不需要求路径
14 finalPath[v0] = 1;
15 for (int i = 1; i < m.numVertexes; i++) {
16 // 当前所知的离V0最近的距离
17 int min = INFINITY;
18 int k = 0;
19 for (int w = 0; w < m.numVertexes; w++) {
20 if(shortPath[w] < min && finalPath[w] == 0) {
21 min = shortPath [w];
22 k = w;
23 }
24 }
25 finalPath[k] = 1; // 修改finalPath的值,标记为已经找到最短路径
26 for (int w = 0; w < m.numVertexes; w++) {
27 // 如果经过V顶点的路径比原来的路径(不经过V)短的话
28 if(finalPath[w] == 0 && (min + m.arc[k][w]) < shortPath[w]) {
29 // 说明找到了更短的路径,修改
30 shortPath[w] = min + m.arc[k][w]; // 修改路径的长度
31 pathSrc[w] = k; // 修改顶点下标W的前驱顶点
32 }
33 }
34 }
35}
1-14:初始化数组,把顶点V0加入到数组中,读取V0与各顶点之间的距离到shortPath
15-34:循环获取V0到各顶点的最短路径
16-24:获取shortPath中的最短路径并标记顶点下标为k,最小值为min。
25:设置顶点K已找到最短路径
26-34:循环获取顶点k到其他顶点的路径(不获取已找到最短路径的顶点),如果从源点V0到顶点W的距离(经过顶点k)比原来距离(不经过顶点k)短(V0->Vk的路径+Vk->Vw的顶点小于shortPath的路径),就修改shortPath的路径以及顶点Vk的前驱顶点。
执行流程
V0->V1:
当前shortPath数组为{0,1,5,65535,65535,65535,65535,65535,65535}。循环shortPath数组得到K=1,min=1;由k=1,表示与V0最近的顶点是V1,由shortPath[1]=1知道此时V0到V1的最短距离是1。将V1对应的finalPath[1]设置为 1。此时finalPath数组为{1,1,0,0,0,0,0,0,0}。
V1->V2:
假设已经找到V0与V1的最短路径的基础上,对V1与其他顶点的边进行计算,得到V0与它们的当前最短距离,因为min=1,所以本来V0->V2=shortPath[2]=5, 现在V0->V1->V2=shortPath[2]=min+3=4, V0->V1->V3=shortPath[3]=min+7=8, V0->V1->v4=shortPath[4]=min+5=6,因此,shortPath数组当前值为{0,1,4,8,6,65535,65535,65535,65535}。而pathSrc[2]=1 ,pathSrc[3]=1,pathSrc[4]=1, 它表示的意思是V0到V2、V3、V4 点的最短路径的前驱均是V1。此时pathSrc数组值为: {0,0,1,1,1,0,0,0,0}。
V2-V3:
在刚才已经找到V0与V2的最短路径的基础上,对V2与其他顶点的边,进行计算,得到V0与它们的当前最短距离,因为min=4, 所以本来shortPath[4]=6, 现在V0→V2→V4=shortPath[4]=min+1=5,V0→V2→V5=shortPath[5]=min+7=11,因此,shortPath数组当前值为: {0,1,4,8,5,11,65535,65535,65535}。而原本pathSrc[4]=1,此时pathSrc[4]=2,pathSrc[5]=2, 它表示V0到V4、V5的最短路径它们的前驱均是V2。此时pathSrc数组值为: {0,0,1,1,2,2,0,0,0}。
之后的循环就完全类似了
结果
- finalPath数组: [1,1,1,1,1,1,1,1,1]。它表示所有的顶点均完成了最短路径的查找工作。
- shortPath数组: [0,1,4,7,5,8,10,12,16]。它表示V0到各个顶点的最短路径数。比如D[8]=1+3+1+2+3+2+4=16。
- pathSrc数组为: [0,0,1,4,2,4,3,6,7]。比如P[8]=7,它的意思是V0到V8的最短路径,顶点V8的前驱顶点是V7,再由P[7]=6 表示V7的前驱是V6, P[6]=3,表 示V6的前驱是V3。以此类推,V0到V8的最短路径V0→V1→V2→V4→V3→V6→V7→V8。
最终返回的数组pathSrc和数组shortPath是可以得到V0到任意顶点的最短路径和路径长度。
例如D[5]=8可知它的路径长度为8,由P[5]=4可知V5的前驱顶点是V4,所以V0到V5的最短路径是V0→V1→V2→V4→V5。
通过迪杰斯特拉(Dijkstra) 算法解决了从某个源点到其余各顶点的最短路径问题。算法的时间复杂度为0(n2)。
当我们需要知道如V3到V5、V1到V7这样的任一顶点到其余所有顶点的最短路径怎么办呢?此时简单的办法就是对每个顶点当作源点运行一次迪杰斯特拉 (Dijkstra)算法,等于在原有算法的基础上再来一次循环,此时整个算法的时间复杂度就成了0(n3)。
费洛伊德算法(Floyd)
解决所有顶点到所有顶点的最短路径问题
它求所有顶点到所有顶点的时间复杂度也是0(n3),但其算法非常简洁优雅,能让人感觉到智慧的无限魅力。
书看不下去了,太难了,明天也该上班了,有空再看