单源最短路径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到集合外顶点】最短的一条路径
- A->B,直通A->B = 无穷, A->C->B = 10 + 无穷 = 无穷,均无通路,则前驱顶点不变
- A->D,直通A->D = 无穷,A->C->D = 10+50 = 60,则A->C->D更短,故修改D的前驱顶点为C
- A->E,直通A->E = 30, A->C->E = 10 + 无穷 = 无穷,则直通A->E更短,前驱顶点不变
- 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