数据结构与算法--图的应用之最短路径

数据结构与算法--图的应用之最短路径

      • 前言
      • 1. Dijkstra 算法
      • 2. 佛洛依德(Floyd)算法

前言

  上一篇简单的介绍了图的应用之最小生成树的两种求解方法,本篇来了解一下关于 图的最短路径 的两种求解方法

首先看下面的图,

数据结构与算法--图的应用之最短路径_第1张图片

从上图中可以很直观的看出:

最短路径:V0 -> V1 -> V2 -> V4 -> V3 -> V6 -> V7 -> V8
最短路径权值和: 1 + 3 + 1 + 2 + 3 + 2 + 4 = 16

那么最短路径怎么求解呢?接下来Dijkstra算法

1. Dijkstra 算法

首先,我们可以将上面的图,存储到 邻接矩阵 中,如下:

因为是无向图,所以是对称的

数据结构与算法--图的应用之最短路径_第2张图片

我们知道,从 V0V1 的权重是 1V0V2的权重是5V1V2的权重是3

那么,可以先从V0V1,再从V1V2,总权重是4,优于直接从 V0V2

因此,我们可以修改V0V2 的权重为4,路径为 V0 -> V1 -> V2,但是我们并不能直接修改 邻接矩阵

所以,可以定义一个 数组D,来表示V0 到某个顶点 Vw 的路径。

定义一个数组final,来标识V0Vw直接是否有最短路径,数组final[w] = 1,表示V0Vw有最短路径

定义一个 数组P,来记录当前顶点前驱顶点下标

思路:

1. 定义 数组D 表示V0 到Vw 的路径,数组final 表示V0 到Vw 是否求得最短路径(1表示已经求得)
   ,数组P 表示当前顶点的前驱顶点的下标(最后根据 P 数组,打印出V0到 Vw 的路径)
2. 初始化三个数组:
    数组D:V0 和其他顶点的路径
    数组P:先默认所有顶点的前驱都是V0(然后遍历修改前驱的下标)
    数组final: 未知最短路径状态 0
3. V0 到 V0的路径为0,D[0] = 0, V0 到 V0的没有路径,final[v0] = 1,
   P[V0] = -1(-1表示没有路径)
4. 开始循环,找到离 V0 最近的顶点,即从 数组D 中找到距离V0权重最小的顶点的下标 k,并记录最小权重
5. 将目前找到最近的顶点,标记为1,表示到V0有最小路径
6. 在找到V0到Vk最短路径的基础上,对于 Vk 与其他顶点的边进行计算,当其小于 V0 直接到某顶点的距离且该顶点未被标记为有最小路径时,更新V0到某顶点的距离,得到V0与它们的当前最短距离。

代码实现:

#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0

#define MAXEDGE 20
#define MAXVEX 20
#define INFINITYC 65535

typedef int Status;

typedef struct {
    int vexs[MAXVEX];
    int arc[MAXVEX][MAXVEX];
    int numVertexes, numEdges;
}MGraph;
//存储最短路径下标的数组
typedef int Patharc[MAXVEX];
//存储到各点最短路径权值的和
typedef int ShortPathTable[MAXVEX];

void CreateMGraph(MGraph *G)
{
    int i, j;
    
    G->numEdges=16;
    G->numVertexes=9;
    
    for (i = 0; i < G->numVertexes; i++)
    {
        G->vexs[i]=i;
    }
    
    for (i = 0; i < G->numVertexes; i++)
    {
        for ( j = 0; j < G->numVertexes; j++)
        {
            if (i==j)
                G->arc[i][j]=0;
            else
                G->arc[i][j] = G->arc[j][i] = INFINITYC;
        }
    }
    
    G->arc[0][1]=1;
    G->arc[0][2]=5;
    G->arc[1][2]=3;
    G->arc[1][3]=7;
    G->arc[1][4]=5;
    
    G->arc[2][4]=1;
    G->arc[2][5]=7;
    G->arc[3][4]=2;
    G->arc[3][6]=3;
    G->arc[4][5]=3;
    
    G->arc[4][6]=6;
    G->arc[4][7]=9;
    G->arc[5][7]=5;
    G->arc[6][7]=2;
    G->arc[6][8]=7;
    
    G->arc[7][8]=4;
    
    
    for(i = 0; i < G->numVertexes; i++)
    {
        for(j = i; j < G->numVertexes; j++)
        {
            G->arc[j][i] =G->arc[i][j];
        }
    }
    
}
 

void ShortestPath_Dijkstra(MGraph G, int v0, Patharc *P, ShortPathTable *D) {
    
    int v,w,k,min = 0;
    
    k = 0;
    // 定义数组,标记到V0是否有最短路径,1有最短路径,0为未知d最短路径
    int final[MAXVEX];
    
    // 初始化
    for (v = 0; v < G.numVertexes; v++) {
        // 默认未知最短路径状态 0,
        final[v] = 0;
        // 将 V0 与其他有连接的顶点的路径赋值给D
        (*D)[v] = G.arc[0][v];
        //初始化路径数组p = 0;
        (*P)[v] = 0;
    }
    
    // V0到V0 是没有路径1
    final[v0] = 1;
    // 没有前驱,前驱下标为-1,
    (*P)[v0] = -1;
    // V0 到 V0 路径为0,
    (*D)[v0] = 0;
    
    // ✅开始主循环
    for (v = 1; v < G.numVertexes; v++) {
        min = INFINITYC;
        // ✅寻找离V0最近的顶点
        for (w = 0; w < G.numVertexes; w++) {
            if (!final[w] && (*D)[w] < min) {
                // 找到,修改最小值,并记录最小顶点下标
                k = w;
                min = (*D)[w];
            }
        }
        // ✅修改标记,标识下标为k的顶点到V0有最小路径
        final[k] = 1;
        
        // ✅在V0到Vk的基础上,对于Vk到其他顶点的边进行计算,
        for (w = 0; w < G.numVertexes; w++) {
            // ✅V0 到Vk + Vk到Vw 的权重 < V0 直接到Vw的路径,更新V0 直接到Vw的路径
            // 并记录顶点Vw的前驱顶点下标
            if (!final[w] && (min + G.arc[k][w] < (*D)[w])) {
                (*D)[w] = min + G.arc[k][w];
                (*P)[w] = k;
            }
        }
    }

}

// 调用
void show() {
    int i,j,v0;
    MGraph G;
    Patharc P;
    ShortPathTable D;
    v0=0;
    
    CreateMGraph(&G);
    
    ShortestPath_Dijkstra(G, v0, &P, &D);
    
    printf("最短路径路线:\n");
    for(i=1;i v%d : ",v0,i);
        j=i;
        while(P[j]!=-1)
        {
            printf("%d ",P[j]);
            j=P[j];
        }
        printf("\n");
    }
    
    printf("\n最短路径权值和\n");
    for(i=1;i v%d : %d \n",G.vexs[0],G.vexs[i],D[i]);
    
    printf("\n");
}

执行流程分析:

定义并初始化数组如下:

final 数组:(标记V0和其他顶点是否有最短路径)

0 1 2 3 4 5 6 7 8
1 0 0 0 0 0 0 0 0

数组 D:(V0到某个顶点Vw的最短路径)

0 1 2 3 4 5 6 7 8
0 1 5

数组 P:(当前顶点的前驱的下标)

0 1 2 3 4 5 6 7 8
-1 0 0 0 0 0 0 0 0

第一次执行主循环

final 数组:(标记V0和其他顶点是否有最短路径)

0 1 2 3 4 5 6 7 8
1 1 0 0 0 0 0 0 0

数组 D(旧):(V0到某个顶点Vw的最短路径)

0 1 2 3 4 5 6 7 8
0 1 5

数组 D(新):(V0到某个顶点Vw的最短路径)

0 1 2 3 4 5 6 7 8
0 1 4 8 6

数组 P(旧):(当前顶点的前驱的下标)

0 1 2 3 4 5 6 7 8
-1 0 0 0 0 0 0 0 0

数组 P(新):(当前顶点的前驱的下标)

0 1 2 3 4 5 6 7 8
-1 0 1 1 1 0 0 0 0
先找到最小下标k = 1, min = 1,基于Vk,寻找 V0 到Vw 的最小路径
1. w = 0, final[0] = 1,不满足条件
2. w = 1, final[1] = 1,不满足条件
3. w = 2, final[2] = 0,G.arc[1][2] = 3,min + G.arc[1][2] < D[2](1 + 3 < 5),
满足,找到V0 -> V2最短路径,更新D[2] = 4, 更新顶点 V2 前驱顶点的下标,P[2] = 1
4. w = 3,final[3] = 0,G.arc[1][3] = 7,min + G.arc[1][3] < D[3](1 + 7 < ∞),
满足,找到V0 -> V3最短路径,更新D[3] = 8, 更新顶点 V3 前驱顶点的下标,P[3] = 1
5. w = 4,final[4] = 0,G.arc[1][4] = 5,min + G.arc[1][4] < D[4](1 + 5 < ∞),
满足,找到V0 -> V4最短路径,更新D[4] = 6, 更新顶点 V4 前驱顶点的下标,P[4] = 1
6. w = 5, final[5] = 0,G.arc[1][1] = 0,min + G.arc[1][5] < D[5](1 + ∞ < ∞),不满足条件
7. 同上,当w = 6,w = 7,w = 8时,条件不成立

第二次执行主循环

final 数组:(标记V0和其他顶点是否有最短路径)

0 1 2 3 4 5 6 7 8
1 1 1 0 0 0 0 0 0

数组 D(旧):(V0到某个顶点Vw的最短路径)

0 1 2 3 4 5 6 7 8
0 1 4 8 6

数组 D(新):(V0到某个顶点Vw的最短路径)

0 1 2 3 4 5 6 7 8
0 1 4 8 5 11

数组 P(旧):(当前顶点的前驱的下标)

0 1 2 3 4 5 6 7 8
-1 0 1 1 1 0 0 0 0

数组 P(新):(当前顶点的前驱的下标)

0 1 2 3 4 5 6 7 8
-1 0 1 1 2 2 0 0 0
先找到最小下标k = 2, min = 4,基于Vk,寻找 V0 到Vw 的最小路径
1. w = 0, final[0] = 1,不满足条件
2. w = 1, final[1] = 1,不满足条件
3. w = 2, final[2] = 1,不满足条件
4. w = 3,final[3] = 0,G.arc[2][3] = ∞,min + G.arc[2][3] < D[3](4 + ∞ < 8),
条件不成立
5. w = 4,final[4] = 0,G.arc[2][4] = 1,min + G.arc[2][4] < D[4](4 + 1 < 6),
满足,找到V0 -> V4最短路径,更新D[4] = 5, 更新顶点 V4 前驱顶点的下标,P[4] = 2
6. w = 5, final[5] = 0,G.arc[2][5] = 7,min + G.arc[1][5] < D[5](4 + 7 < ∞),
满足,找到V0 -> V5最短路径,更新D[5] = 11, 更新顶点 V5 前驱顶点的下标,P[5] = 2
7. w = 6,final[6] = 0,G.arc[2][6] = ∞,min + G.arc[2][6] < D[3](4 + ∞ < ∞),条件不成立
8. 同上,当w = 7,w = 8时,条件不成立

第三次执行主循环

final 数组:(标记V0和其他顶点是否有最短路径)

0 1 2 3 4 5 6 7 8
1 1 1 0 1 0 0 0 0

数组 D(旧):(V0到某个顶点Vw的最短路径)

0 1 2 3 4 5 6 7 8
0 1 4 8 5 11

数组 D(新):(V0到某个顶点Vw的最短路径)

0 1 2 3 4 5 6 7 8
0 1 4 7 5 8 11 14

数组 P(旧):(当前顶点的前驱的下标)

0 1 2 3 4 5 6 7 8
-1 0 1 1 2 2 0 0 0

数组 P(新):(当前顶点的前驱的下标)

0 1 2 3 4 5 6 7 8
-1 0 1 4 2 4 4 4 0
先找到最小下标k = 4, min = 5,基于Vk,寻找 V0 到Vw 的最小路径
1. w = 0, final[0] = 1,不满足条件
2. w = 1, final[1] = 1,不满足条件
3. w = 2, final[2] = 1,不满足条件
4. w = 3,final[3] = 0,G.arc[4][3] = 2,min + G.arc[4][3] < D[3](5 + 2 < 8),
满足,找到V0 -> V3最短路径,更新D[3] = 7, 更新顶点 V3 前驱顶点的下标,P[3] = 4
5. w = 4, final[4] = 1,不满足条件
6. w = 5, final[5] = 0,G.arc[4][5] = 3,min + G.arc[4][5] < D[5](5 + 3 < 11),
满足,找到V0 -> V5最短路径,更新D[5] = 8, 更新顶点 V5 前驱顶点的下标,P[5] = 4
7. w = 6,final[6] = 0,G.arc[4][6] = 6,min + G.arc[4][6] < D[6](5 + 6 < ∞),
满足,找到V0 -> V6最短路径,更新D[6] = 11, 更新顶点 V6 前驱顶点的下标,P[6] = 4
8. w = 7,final[7] = 0,G.arc[4][7] = 9,min + G.arc[4][7] < D[7](5 + 9 < ∞),
满足,找到V0 -> V7最短路径,更新D[7] = 14, 更新顶点 V7 前驱顶点的下标,P[7] = 4
9. w = 8,final[8] = 0,G.arc[4][8] = ∞,min + G.arc[4][8] < D[7](5 + ∞ < ∞),不满足条件

第四次执行主循环

final 数组:(标记V0和其他顶点是否有最短路径)

0 1 2 3 4 5 6 7 8
1 1 1 1 1 0 0 0 0

数组 D(旧):(V0到某个顶点Vw的最短路径)

0 1 2 3 4 5 6 7 8
0 1 4 7 5 8 11 14

数组 D(新):(V0到某个顶点Vw的最短路径)

0 1 2 3 4 5 6 7 8
0 1 4 7 5 8 10 14

数组 P(旧):(当前顶点的前驱的下标)

0 1 2 3 4 5 6 7 8
-1 0 1 4 2 4 4 4 0

数组 P(新):(当前顶点的前驱的下标)

0 1 2 3 4 5 6 7 8
-1 0 1 4 2 4 3 4 0
先找到最小下标k = 3, min = 7,基于Vk,寻找 V0 到Vw 的最小路径
1. w = 0, final[0] = 1,不满足条件
2. w = 1, final[1] = 1,不满足条件
3. w = 2, final[2] = 1,不满足条件
4. w = 3,final[3] = 1,不满足条件
5. w = 4, final[4] = 1,不满足条件
6. w = 5, final[5] = 0,G.arc[3][5] = ∞,min + G.arc[3][5] < D[5](7 + ∞ < 8),不满足条件
7. w = 6,final[6] = 0,G.arc[3][6] = 3,min + G.arc[3][6] < D[6](7 + 3 < 11),
满足,找到V0 -> V6最短路径,更新D[6] = 10, 更新顶点 V6 前驱顶点的下标,P[6] = 3
8. w = 7,final[7] = 0,G.arc[3][7] = ∞,min + G.arc[3][7] < D[7](7 + ∞ < 14),不满足条件
9. w = 8,final[8] = 0,G.arc[3][8] = ∞,min + G.arc[3][8] < D[7](7 + ∞ < ∞),不满足条件

第五次执行主循环

final 数组:(标记V0和其他顶点是否有最短路径)

0 1 2 3 4 5 6 7 8
1 1 1 1 1 1 0 0 0

数组 D(旧):(V0到某个顶点Vw的最短路径)

0 1 2 3 4 5 6 7 8
0 1 4 7 5 8 10 14

数组 D(新):(V0到某个顶点Vw的最短路径)

0 1 2 3 4 5 6 7 8
0 1 4 7 5 8 10 13

数组 P(旧):(当前顶点的前驱的下标)

0 1 2 3 4 5 6 7 8
-1 0 1 4 2 4 3 4 0

数组 P(新):(当前顶点的前驱的下标)

0 1 2 3 4 5 6 7 8
-1 0 1 4 2 4 3 5 0
先找到最小下标k = 5, min = 8,基于Vk,寻找 V0 到Vw 的最小路径
1. w = 0, final[0] = 1,不满足条件
2. w = 1, final[1] = 1,不满足条件
3. w = 2, final[2] = 1,不满足条件
4. w = 3,final[3] = 1,不满足条件
5. w = 4, final[4] = 1,不满足条件
6. w = 5, final[5] = 1,不满足条件
7. w = 6,final[6] = 0,G.arc[5][6] = ∞,min + G.arc[3][6] < D[6](8 + ∞ < 11),不满足条件
8. w = 7,final[7] = 0,G.arc[5][7] = 5,min + G.arc[3][7] < D[7](8 + 5 < 14),
满足,找到V0 -> V7最短路径,更新D[7] = 13, 更新顶点 V7 前驱顶点的下标,P[7] = 5
9. w = 8,final[8] = 0,G.arc[5][8] = ∞,min + G.arc[3][8] < D[7](8 + ∞ < ∞),不满足条件

第六次执行主循环

final 数组:(标记V0和其他顶点是否有最短路径)

0 1 2 3 4 5 6 7 8
1 1 1 1 1 1 1 0 0

数组 D(旧):(V0到某个顶点Vw的最短路径)

0 1 2 3 4 5 6 7 8
0 1 4 7 5 8 10 13

数组 D(新):(V0到某个顶点Vw的最短路径)

0 1 2 3 4 5 6 7 8
0 1 4 7 5 8 10 12 17

数组 P(旧):(当前顶点的前驱的下标)

0 1 2 3 4 5 6 7 8
-1 0 1 4 2 4 3 5 0

数组 P(新):(当前顶点的前驱的下标)

0 1 2 3 4 5 6 7 8
-1 0 1 4 2 4 3 6 6
先找到最小下标k = 6, min = 10,基于Vk,寻找 V0 到Vw 的最小路径
1. w = 0, final[0] = 1,不满足条件
2. w = 1, final[1] = 1,不满足条件
3. w = 2, final[2] = 1,不满足条件
4. w = 3,final[3] = 1,不满足条件
5. w = 4, final[4] = 1,不满足条件
6. w = 5, final[5] = 1,不满足条件
7. w = 6,final[6] = 1,不满足条件
8. w = 7,final[7] = 0,G.arc[6][7] = 2,min + G.arc[6][7] < D[7](10 + 2 < 13),
满足,找到V0 -> V7最短路径,更新D[7] = 12, 更新顶点 V7 前驱顶点的下标,P[7] = 6
9. w = 8,final[8] = 0,G.arc[6][8] = 7,min + G.arc[6][8] < D[7](10 + 7 < ∞),足条件
满足,找到V0 -> V8最短路径,更新D[8] = 17, 更新顶点 V8 前驱顶点的下标,P[8] = 6

第七次执行主循环

final 数组:(标记V0和其他顶点是否有最短路径)

0 1 2 3 4 5 6 7 8
1 1 1 1 1 1 1 1 0

数组 D(旧):(V0到某个顶点Vw的最短路径)

0 1 2 3 4 5 6 7 8
0 1 4 7 5 8 10 12 17

数组 D(新):(V0到某个顶点Vw的最短路径)

0 1 2 3 4 5 6 7 8
0 1 4 7 5 8 10 12 16

数组 P(旧):(当前顶点的前驱的下标)

0 1 2 3 4 5 6 7 8
-1 0 1 4 2 4 3 6 6

数组 P(新):(当前顶点的前驱的下标)

0 1 2 3 4 5 6 7 8
-1 0 1 4 2 4 3 6 7
先找到最小下标k = 7, min = 12,基于Vk,寻找 V0 到Vw 的最小路径
1. w = 0, final[0] = 1,不满足条件
2. w = 1, final[1] = 1,不满足条件
3. w = 2, final[2] = 1,不满足条件
4. w = 3,final[3] = 1,不满足条件
5. w = 4, final[4] = 1,不满足条件
6. w = 5, final[5] = 1,不满足条件
7. w = 6,final[6] = 1,不满足条件
8. w = 7,final[7] = 1,不满足条件
9. w = 8,final[8] = 0,G.arc[7][8] = 4,min + G.arc[7][8] < D[7](12 + 4 < 17),足条件
满足,找到V0 -> V8最短路径,更新D[8] = 16, 更新顶点 V8 前驱顶点的下标,P[8] = 7

第八次执行主循环
先找到最小下标k = 8, min = 16,设置final[8] = 1,如下,所以不会在更新数组D数组P,最终得到数组P
final 数组:(标记V0和其他顶点是否有最短路径)

0 1 2 3 4 5 6 7 8
1 1 1 1 1 1 1 1 1

数组 D(新):(V0到某个顶点Vw的最短路径)

0 1 2 3 4 5 6 7 8
0 1 4 7 5 8 10 12 16

数组 P(新):(当前顶点的前驱的下标)

0 1 2 3 4 5 6 7 8
-1 0 1 4 2 4 3 6 7
先找到最小下标k = 7, min = 12,基于Vk,寻找 V0 到Vw 的最小路径
1. w = 0, final[0] = 1,不满足条件
2. w = 1, final[1] = 1,不满足条件
3. w = 2, final[2] = 1,不满足条件
4. w = 3,final[3] = 1,不满足条件
5. w = 4, final[4] = 1,不满足条件
6. w = 5, final[5] = 1,不满足条件
7. w = 6,final[6] = 1,不满足条件
8. w = 7,final[7] = 1,不满足条件
9. w = 8,final[8] = 1,不满足条件

最后根据数组P数组D,求得V0到某个顶点的最短路径和权重,

在求最短路径时,遍历数组p,当前驱下标等-1 时,表示找到源点。

2. 佛洛依德(Floyd)算法

如下图:
数据结构与算法--图的应用之最短路径_第3张图片

上图的邻接矩阵

D V0 V1 V2
V0 0 2 1
V1 2 0 5
V2 1 5 0

V1 -> V2 的最短路径是什么?

直接从V1 -> V2 = 5,但是从V1 -> V0 -> V2 = 3

那么我们可以定义一个 邻接矩阵 D,代表顶点到顶点的最短路径的矩阵

初始化 邻接矩阵 D 和 图的邻接矩阵一致,

然后判断 D[1][2] > D[1][0]+D[0][2] 时,说明 顶点V1顶点V2,之间 有更短的路径,此时更新 D[1][2] 位置上的权重(即V1到顶点V2最短路径的总权重)。

同时,定义一个 二维数组P,代表对应顶点最短路路径的前驱矩阵

当更新某位置的权重时,同时更新 前驱矩阵 的对应位置为其前驱节点下标(比如上面的P[1][2] = P[1][0])。

佛洛依德(Floyd)算法公式

D[V][W] = min{ D[V][W], D[V][K]+D[K][W] }

P[V][W] = P[V][K]

其中,k为从 V 到 W 的中转点。

佛洛依德(Floyd)算法代码实现:

#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#define MAXEDGE 20
#define MAXVEX 20
#define INFINITYC 65535

typedef int Status;    /* Status是函数的类型,其值是函数结果状态代码,如OK等 */

typedef struct
{
   int vexs[MAXVEX];
   int arc[MAXVEX][MAXVEX];
   int numVertexes, numEdges;
}MGraph;

typedef int Patharc[MAXVEX][MAXVEX];
typedef int ShortPathTable[MAXVEX][MAXVEX];

/* 11.1 构成邻近矩阵 */
void CreateMGraph(MGraph *G)
{
   int i, j;
   
   /* printf("请输入边数和顶点数:"); */
   G->numEdges=16;
   G->numVertexes=9;
   
   for (i = 0; i < G->numVertexes; i++)/* 初始化图 */
   {
       G->vexs[i]=i;
   }
   
   for (i = 0; i < G->numVertexes; i++)/* 初始化图 */
   {
       for ( j = 0; j < G->numVertexes; j++)
       {
           if (i==j)
               G->arc[i][j]=0;
           else
               G->arc[i][j] = G->arc[j][i] = INFINITYC;
       }
   }
   
   G->arc[0][1]=1;
   G->arc[0][2]=5;
   G->arc[1][2]=3;
   G->arc[1][3]=7;
   G->arc[1][4]=5;
   
   G->arc[2][4]=1;
   G->arc[2][5]=7;
   G->arc[3][4]=2;
   G->arc[3][6]=3;
   G->arc[4][5]=3;
   
   G->arc[4][6]=6;
   G->arc[4][7]=9;
   G->arc[5][7]=5;
   G->arc[6][7]=2;
   G->arc[6][8]=7;
   
   G->arc[7][8]=4;
   
   
   for(i = 0; i < G->numVertexes; i++)
   {
       for(j = i; j < G->numVertexes; j++)
       {
           G->arc[j][i] =G->arc[i][j];
       }
   }
   
}


/// Floyd算法
/// @param G 图
/// @param P 前驱矩阵
/// @param D 最短路径矩阵
void ShortestPath_Floyd(MGraph G, Patharc *P, ShortPathTable *D)
{
   int w, v, k;
   
   // ✅初始化P和D
   for (v = 0; v < G.numVertexes; ++v) {
       for (w = 0; w < G.numVertexes; ++w) {
           // ✅D[v][w]值即为对应点间的权值
           (*D)[v][w] = G.arc[v][w];
           // ✅初始化P P[v][w] = w
           (*P)[v][w] = w;
       }
   }
   // ✅K表示经过的中转顶点
   for (k = 0; k < G.numVertexes; ++k) {
       for (v = 0; v < G.numVertexes; ++v) {
           for (w = 0; w < G.numVertexes; ++w) {
               if ((*D)[v][w] > (*D)[v][k] + (*D)[k][w]) {
                   // ✅将当前两点间权值设为更小的一个
                   (*D)[v][w] = (*D)[v][k] + (*D)[k][w];
                   // ✅路径设置为经过下标为k的顶点
                   (*P)[v][w] = (*P)[v][k];
               }
           }
       }
   }
   
}

// 调用
void show(){
   int v,w,k;
   MGraph G;
   
   Patharc P;
   ShortPathTable D; /* 求某点到其余各点的最短路径 */
   
   CreateMGraph(&G);
   
   ShortestPath_Floyd(G,&P,&D);
   
   //打印所有可能的顶点之间的最短路径以及路线值
   printf("各顶点间最短路径如下:\n");
   for(v=0; v %d",k);
               //获得下一个路径顶点下标
               k=P[k][w];
           }
           //打印终点
           printf(" -> %d\n",w);
       }
       printf("\n");
   }
   
   //✅打印最终变换后的最短路径D数组
   printf("最短路径D数组\n");
   for(v=0; v

执行过程分析:

数据结构与算法--图的应用之最短路径_第4张图片

数据结构与算法--图的应用之最短路径_第5张图片
第一次执行:k = 0
当K = 0,所有的顶点都经过V0中转,k = 0,没有任何变化。

第二次执行:k = 1(所有顶点都经过 V1 中转)

v = 0,w = [0 - 8]

 D[0][0] = 0,D[0][1] + D[1][0] = 0,不更新
 D[0][1] = 1,D[0][1] + D[1][1] = 1,不更新
 D[0][2] = 5,D[0][1] + D[1][2] = 1 + 3,4 < 5,更新D[0][2] = 4
 D[0][3] = ∞,D[0][1] + D[1][3] = 1 + 7,8 < ∞,更新D[0][3] = 8
 D[0][4] = ∞,D[0][1] + D[1][4] = 1 + 5, 6 < ∞, 更新D[0][4] = 6
 D[0][5] = ∞,D[0][1] + D[1][5] = 1 + ∞, 不更新
 D[0][6] = ∞,D[0][1] + D[1][6] = 1 + ∞, 不更新
 D[0][7] = ∞,D[0][1] + D[1][7] = 1 + ∞, 不更新
 D[0][8] = ∞,D[0][1] + D[1][8] = 1 + ∞, 不更新

v = 1,w = [0 - 8]

 D[1][0] = 1,D[1][1] + D[1][0] = 1,不更新
 D[1][1] = 0,D[1][1] + D[1][1] = 0,不更新
 D[1][2] = 3,D[1][1] + D[1][2] = 0 + 3,不更新
 D[1][3] = 7,D[1][1] + D[1][3] = 0 + 7,不更新
 D[1][4] = 5,D[1][1] + D[1][4] = 0 + 5, 不更新
 D[1][5] = ∞,D[1][1] + D[1][5] = 0 + ∞, 不更新
 D[1][6] = ∞,D[1][1] + D[1][6] = 0 + ∞, 不更新
 D[1][7] = ∞,D[1][1] + D[1][7] = 0 + ∞, 不更新
 D[1][8] = ∞,D[1][1] + D[1][8] = 0 + ∞, 不更新
 
v = 3,w = [0 - 8]

 D[3][0] = ∞,D[3][1] + D[1][0] = 7 + 1 < ∞,更新D[3][0] = 8
 D[3][1] = 7,D[3][1] + D[1][1] = 7,不更新
 D[3][2] = ∞,D[3][1] + D[1][2] = 7 + 3 < ∞,更新D[3][2] = 10
 D[3][3] = 0,D[3][1] + D[1][3] = 7 + 7 > 0,不更新
 D[3][4] = 2,D[3][1] + D[1][4] = 7 + 5 > 2, 不更新
 D[3][5] = ∞,D[3][1] + D[1][5] = 7 + ∞, 不更新
 D[3][6] = 3,D[3][1] + D[1][6] = 7 + ∞, 不更新
 D[3][7] = ∞,D[3][1] + D[1][7] = 7 + ∞, 不更新
 D[3][8] = ∞,D[3][1] + D[1][8] = 7 + ∞, 不更新

按照上面的规律,依次循环遍历,最终得到:

数据结构与算法--图的应用之最短路径_第6张图片

数据结构与算法--图的应用之最短路径_第7张图片

然后根据数组D前驱矩阵P打印顶点和顶点之间的最短路径

你可能感兴趣的:(数据结构与算法)