单源最短路径Dijkstra算法

单源最短路径迪杰斯特拉(Dijkstra)算法,用于求解图结构中,起点v0到终点ve的最短路径

1. 图文解析

已知邻接矩阵G,存在集合S和三个辅助数组Flag、MinLen、Path
(1)Flag用于表示对应顶点是否加入集合S(0/1)
(2)MinLen用于存储源点v0到其他各个顶点的最小边权值
(3)Path用于记录每个顶点的最短路径前驱顶点下标

则求解最短路径的过程如下:
(1)选择起始顶点A加入集合S中(Flag[0] = 1),初始化MinLen数组A到各个顶点的距离(例如A->C = 10),初始化Path的直接前驱顶点下标(例如C的直接前驱为A,D没有直接的前驱-1)
在这里插入图片描述
(2)选择集合S到其他各个顶点的最小边(10)对应顶点C,并将顶点C加入集合S
在这里插入图片描述
(3)更新MinLen数组,选择【源点A直接到集合外顶点的距离】 和 【源点A通过最短边路径A-C到集合外顶点】最短的一条路径

  1. A->B,直通A->B = 无穷, A->C->B = 10 + 无穷 = 无穷,均无通路,则前驱顶点不变
  2. A->D,直通A->D = 无穷,A->C->D = 10+50 = 60,则A->C->D更短,故修改D的前驱顶点为C
  3. A->E,直通A->E = 30, A->C->E = 10 + 无穷 = 无穷,则直通A->E更短,前驱顶点不变
  4. A->F,直通A->F = 100, A->C->F = 10 + 无穷 = 无穷,则直通A->F更短,前驱顶点不变

在这里插入图片描述
重复第二三步依次推导,【选择集合外最小边顶点】 + 【更新最短路径长度和前驱顶点】
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2. 源代码

#include<stdio.h>
#include<stdlib.h>
#define MaxVex 10
#define INF 65535

typedef char VertexType;    // 顶点的数据类型
typedef int EdgeType;       // 边的数据类型
typedef struct
{
    VertexType *vertexs;    // 一维数组存放顶点数据
    EdgeType **edges;       // 二维数组存放边的数据
    int numVertexs, numEdges;//顶点数、边数
}AMGraph;

// 边结构
typedef struct Edge
{
    EdgeType edgeData;
    int headVertex, tailVertex;
}Edge;

// 初始化领接矩阵
void InitAMGraph(AMGraph *G)
{
    // 初始化顶点和边的数量
    G->numVertexs = 0;
    G->numEdges = 0;
    // 初始化顶点一维数组
    G->vertexs = (VertexType *)malloc(MaxVex*sizeof(VertexType));
    // 初始化边的二维数组
    int i, j;
    G->edges = (EdgeType **)malloc(MaxVex*sizeof(EdgeType *));
    for (i = 0; i < MaxVex; i++)
    {
        G->edges[i] = (EdgeType *)malloc(MaxVex*sizeof(EdgeType));
        // 初始化每条边都为INF(无穷大)
        for ( j = 0; j < MaxVex; j++)
        {
            if (i == j)
            {
                G->edges[i][j] = 0;     // 对角线初始位为0
            }
            else
            {
                G->edges[i][j] = INF;   // 初始化为无穷大
            }
        }
    }
    
    printf("已初始化邻接矩阵!\n");
}   

// 创建领接矩阵
void CreateAMGraph(AMGraph *G)
{
    printf("请输入顶点数和边数:");
    scanf("%d %d", &G->numVertexs, &G->numEdges);

    int i, j, k, weight;
    // 输入顶点数据
    for (i = 0; i < G->numVertexs; i++)
    {
        fflush(stdin);
        printf("请输入第%d个顶点数据:", i + 1);
        scanf("%c", &G->vertexs[i]);
    }
    // 输入边的权值
    for (k = 0; k < G->numEdges; k++)
    {
        printf("请输入第%d条边的两顶点及其权值:", k + 1);
        scanf("%d %d %d", &i, &j, &weight);
        G->edges[i - 1][j - 1] = weight;
    }
    printf("已完成邻接矩阵的创建!\n");
}


void ShortestPath_DIJ(AMGraph G, int v0, int ve)
{
    int Flag[MaxVex];   // 标志每个顶点是否加入最短路径的顶点集合S中

    EdgeType MinLen[MaxVex];	// 表示从源顶点到各个顶点最短路径长度

    int Path[MaxVex];	// 表示最短路径中各个顶点的前驱顶点

    int i, v, w, min;
    for (v = 0; v < G.numVertexs; v++)
    {
        Flag[v] = 0;                // 初始化所有顶点都未加入集合S 
        MinLen[v] = G.edges[v0][v]; // 初始化v0到各个顶点的最短距离
        if (MinLen[v] < INF)
        {
            Path[v] = v0;   // 源点到对应下标顶点有直接通路
        }
        else
        {
            Path[v] = -1;   // 源点到对应下标顶点无直接通路
        }  
    }

    Flag[v0] = 1;   // 源点本身加入集合S
    MinLen[v0] = 0; // 源点到本身的距离为0
    Path[v0] = -1;   // 源点本身没有前驱顶点

    for (i = 0; i < G.numVertexs; i++)
    {
        min = INF;
        // 遍历所有顶点,找出当前最短的一条边,终点为 w
        for (v = 0; v < G.numVertexs; v++)
        {
            // 获取不在集合S中,最小边权值的尾顶点下标
            if (!Flag[v] && MinLen[v] < min)
            {
                w = v;
                min = MinLen[v];
            }      
        }
        // 将当前最短的边顶点加入集合S中
        Flag[w] = 1;

        // 最短路径集合S加入新顶点后,更新集合S到其他顶点的最小权值
        for (v = 0; v < G.numVertexs; v++)
        {
            // 比较不在集合S中,v0->w->v和v0->v两段路径之间的大小
            if (!Flag[v] && MinLen[w] + G.edges[w][v] < MinLen[v])
            {
                MinLen[v] = MinLen[w] + G.edges[w][v];  // 最短边权值替换为更短的v0->w->v
                Path[v] = w;                            // 设置顶点v的前驱顶点下标为w
            }
        }
    }

    // 打印结果
    v = ve;
    printf("%c-->%c的最小值 = %d\n", G.vertexs[v0], G.vertexs[v], MinLen[v]);
    printf("%c-->%c的最短路径为: %c<---",G.vertexs[v0], G.vertexs[v], G.vertexs[v]);
    while (Path[v] != -1)
    {
        printf("%c<---", G.vertexs[Path[v]]);
        v = Path[v];
    }
    printf("\n");
}

int main()
{
    AMGraph G;
    InitAMGraph(&G);
    CreateAMGraph(&G);
    // 求顶点A--->F的最短路径
    ShortestPath_DIJ(G, 0, 5);
    system("pause");
    return 0;
}

3. 测试结果

#测试用例
6 8
A
B
C
D
E
F
1 3 10
1 5 30
1 6 100
2 3 5
3 4 50
4 6 10
5 4 20
5 6 60

在这里插入图片描述

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐