数据结构(22)图的最短路径
前言
本篇是数据结构系列的最后一篇了,当然,涉及到数据结构还有一些排序算法、搜索算法没有写出来,以后会慢慢补上。
我们说的最短路径,指的是从有向图中,某一顶点A到另一顶点B的最短路径。
这就有两种情况:第一种是不考虑边的权值,而考虑中转次数,求的是A到B最少需要经过几条边。第二种则是考虑边的权值,求从A到B路径上权值之和最短的一条。
如图所示,从A到E的路径有:A-E(100),A-D-C-E(60),A-D-E(90),A-B-C-E(70)等等。假如是第一种情况,需要找到的是A-E边,而第二种情况,需要找到的是A-D-C-E边。
这里我们说的最短路径是第二种情况,所实现的算法为迪杰斯特拉算法。
迪杰斯特拉算法
迪杰斯特拉算法的思路是:
-
从要寻找的起点A开始,维护一个记录到达其他顶点X最短距离的数组dist,若不可达,则存为MAX_VALUE。
-
将数组中拥有最短距离的顶点B纳入A顶点集合,然后去统计 从B到其余顶点X的距离,加上A到B的距离,是否小于之前要到达X的最短距离,若小于,则更新最短距离数组。
注:最短距离数组中存放的可能是A直达X的距离,也可能是A到C,C再到X的距离。总之存储的是当前情况下A到X的最短距离。
-
重复第二步,直到找完最后一个顶点为止。
很显然,当一个顶点被纳入A顶点集合之后,是不会再重复纳入的。这就要求我们再维护一个visited数组,记录当前顶点是否已经被纳入集合。同时,还需要一个path数组记录到达每一个顶点的路径。
直接看例子:
-
求从A出发到达各个顶点的最短路径,此时初始化数组,dist数组中记录的是A到其余顶点边的权值,若无边,则为最大值(表示不可达)。path数组记录了A到达各个顶点的路径,A无法到达自己,记录为-1,C不可达,记录为最大值。而visited数组记录了已经纳入A集合的顶点,此时只有A。
-
从dist数组中找到最短距离的顶点,此时为B,将B纳入A顶点集合(visited[1] = 1)。统计从B到其余顶点的距离,加上A-B的距离10,是否小于dist数组中存储的距离。可以发现,A-B-C为60,小于原来的A-C的最大值,更新dist数组。并且将到达C的路径改为B的下标(path[2] = 1)。
注:path记录了A到其余顶点的路径。path[2] 值为 1,意味着要到达C,首先要到达B(path[1]),而要到达C,首先要到达path[0],path[0] 值为 -1,说明就是从path[0]出发的,这样就可以找到A-B-C的路径。
-
从dist数组中找到最短距离的顶点,此时为D,将D纳入A顶点集合(visited[3] = 1)。统计从D到其余顶点的距离,加上A-D的距离30,是否小于dist数组中存储的距离。可以发现,A-D-E为90,小于原来A-E的最大值;A-D-C为50,也小于原来的A-B-C最大值,更新dist数组。并且修改到达E、C的路径为D的下标。
-
从dist数组中找到最短距离的顶点,此时为C,将C纳入A顶点集合(visited[2] = 1)。统计从C到其余顶点的距离,加上A-C的距离50,是否小于dist数组中存储的距离。可以发现,A-D-C-E为60,小于原来A-D-E的最大值,更新dist数组。并且修改到达E的路径为C的下标。
-
从dist数组中找到最短距离的顶点,此时为E,将E纳入A顶点集合(visited[4] = 1)。统计从E到其余顶点的距离,加上A-E的距离60,是否小于dist数组中存储的距离。此时无可更新的数据。
当整个迪杰斯特拉算法运行结束后,我们就可以得到A到达每一个顶点的最短距离dist[],和路径path[]。
全部代码
Main.c
#include "GraphMtx.h"
int main(int argc, const char * argv[]) {
GraphMtx gm;
InitGraph(&gm);
InsertVertex(&gm,'A');
InsertVertex(&gm,'B');
InsertVertex(&gm,'C');
InsertVertex(&gm,'D');
InsertVertex(&gm,'E');
InsertEdge(&gm,'A','B',10);
InsertEdge(&gm,'A','D',30);
InsertEdge(&gm,'A','E',100);
InsertEdge(&gm,'B','C',50);
InsertEdge(&gm,'C','E',10);
InsertEdge(&gm,'D','C',20);
InsertEdge(&gm,'D','E',60);
ShowGraph(&gm);
int n = gm.NumVertices;
int *dist = (int *)malloc(sizeof(int) * n);
int *path = (int *)malloc(sizeof(int) * n);
assert