数据结构_单源最短路径Dijkstra算法(C语言)

单源最短路径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

在这里插入图片描述

  • 20
    点赞
  • 114
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
单元最短路径算法(Dijkstra算法)是一种用于计算图中最短路径的算法。下面是用C语言编写的Dijkstra算法代码示例: ```c #include <stdio.h> #define INF 99999 void dijkstra(int graph[ ][ ], int n, int start) { int cost[n][n], distance[n], pred[n]; int visited[n], count, mindistance, nextnode, i, j; // 初始化 for(i = 0; i < n; i++) { for(j = 0; j < n; j++) { if(graph[i][j] == 0) cost[i][j] = INF; else cost[i][j] = graph[i][j]; } } for(i = 0; i < n; i++) { distance[i] = cost[start][i]; pred[i] = start; visited[i] = 0; } distance[start] = 0; visited[start] = 1; count = 1; while(count < n - 1) { mindistance = INF; // 找到下一个最短距离的节点 for(i = 0; i < n; i++) { if(distance[i] < mindistance && !visited[i]) { mindistance = distance[i]; nextnode = i; } } visited[nextnode] = 1; // 更新距离数组 for(i = 0; i < n; i++) { if(!visited[i]) { if(mindistance + cost[nextnode][i] < distance[i]) { distance[i] = mindistance + cost[nextnode][i]; pred[i] = nextnode; } } } count++; } // 输出最短路径和距离 for(i = 0; i < n; i++) { if(i != start) { printf("\n距离节点 %d 的最短路径 = %d", i, distance[i]); printf("\n最短路径 = %d", i); j = i; do { j = pred[j]; printf("<-%d", j); }while(j != start); } } } int main() { int graph[5][5] = {{0, 10, 0, 30, 100}, {10, 0, 50, 0, 0}, {0, 50, 0, 20, 10}, {30, 0, 20, 0, 60}, {100, 0, 10, 60, 0}}; int n = 5, start = 0; dijkstra(graph, n, start); return 0; } ``` 这个代码示例中包括了Dijkstra算法的实现,主要是通过使用邻接矩阵来表示图,并利用循环来更新最短路径和距离数组。最后输出了每个节点到起点的最短路径及其距离。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小-黯

免费的,尽力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值