数据结构_单源最短路径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

在这里插入图片描述

Dijkstra算法是一种用于求解单源最短路径问题的贪心算法,以下是用C语言实现的示例代码: ```c #include <stdio.h> #include <stdlib.h> #include <limits.h> #define V 6 // 图中节点的个数 int minDistance(int dist[], int sptSet[]) { int min = INT_MAX, min_index; for (int v = 0; v < V; v++) if (sptSet[v] == 0 && dist[v] <= min) min = dist[v], min_index = v; return min_index; } void printSolution(int dist[]) { printf("Vertex Distance from Source\n"); for (int i = 0; i < V; i++) printf("%d \t\t %d\n", i, dist[i]); } void dijkstra(int graph[V][V], int src) { int dist[V]; int sptSet[V]; for (int i = 0; i < V; i++) dist[i] = INT_MAX, sptSet[i] = 0; dist[src] = 0; for (int count = 0; count < V - 1; count++) { int u = minDistance(dist, sptSet); sptSet[u] = 1; for (int v = 0; v < V; v++) if (!sptSet[v] && graph[u][v] && dist[u] != INT_MAX && dist[u] + graph[u][v] < dist[v]) dist[v] = dist[u] + graph[u][v]; } printSolution(dist); } int main() { int graph[V][V] = { {0, 4, 0, 0, 0, 0}, {4, 0, 8, 0, 0, 0}, {0, 8, 0, 7, 0, 4}, {0, 0, 7, 0, 9, 14}, {0, 0, 0, 9, 0, 10}, {0, 0, 4, 14, 10, 0} }; dijkstra(graph, 0); return 0; } ``` 在这个示例代码中,我们使用了一个邻接矩阵表示图。其中,如果两个节点之间存在一条边,则对应的邻接矩阵元素为边的权重;反之,则为0。 在算法的实现中,首先初始化所有节点到起点的距离为无穷大,起点到自身的距离为0。然后,每次从未标记过的节点中选择到起点距离最短的节点,并将其标记为已访问。接着,更新与该节点直接相连的所有未标记过的节点的最短距离。重复以上步骤,直到所有节点都被标记为已访问。 最后,输出每个节点到起点的最短距离即可。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

小-黯

免费的,尽力

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

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

打赏作者

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

抵扣说明:

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

余额充值