图的最短路径
- 迪杰斯特拉算法
- 贝尔曼-福特算法
- 弗洛伊德算法
- SPFA算法(中国西南交通大学段凡丁发明)
最短路径问题分为两类,一大类是求一个顶点到其余各顶点的最短路径问题,另一大类是求各个顶点间最短路径问题。
迪杰斯特拉
迪杰斯特拉算法就是求解一个点到其余各点的最短路径算法,无向图带权图和有向带权图都适用。缺点是不适用权值为负数的图(后面会讲解原因)
算法步骤
- 初始的点为起点,我们用s集合存储已经确定最短路径的点的集合,那么s={v},起点加入。其余各个点到v点的权值存储在dis数组里,不是直接连接的点距离都是无穷大
- 从dis数组选出一个顶点u,这个点u到v点距离最小,把u加入s集合表示u已经确定了最短路径
- 以点u为中介点,将除了s集合存储的点以外的其余点逐个判断,如果某个点x存在dis[u]+u点到x点距离<dis[x],就更新x的路径。
- 重复2,3步骤,直到s集合包含了所有点。
我们还是直接上图看完整流程
首先我们先准备几个辅助数组,我们假设从点A为起点,找其它点到点A的最短路径
节点 | A | B | C | D | E |
---|---|---|---|---|---|
下标 | 0 | 1 | 2 | 3 | 4 |
权重 | 0 | 4 | ∞ | 2 | ∞ |
标记 | 1 | 0 | 0 | 0 | 0 |
前驱 | -1 | 0 | -1 | 0 | -1 |
我来说明下,这几个数组的作用
权重数组存储每个点到A点的最短距离
前驱数组存储的是每个点前驱点下标,例如B点前驱是0,表示B和A连接,前驱是A
标记数组存储的是当前点是否已经找到了最短路径。
接下来我们准备工作做好了,开始干活了!
1.按照算法步骤,找出没被标记点中权重最小的点,我们找到了D点,将D点标记为1,表示D点以确认最短路径,然后以D点为中介判断未标记点,首先来到了B点,权重数组中B对应的是4,根据算法步骤3,我们计算出权重数组D+BD距离2+1=3 < 4(4就是权重数组B的值)所以B当前有一条路径比之前近,所以修改了B点对应的权重为3,同时更新它的前驱是D的下标。意思就是经过D点到B,更加近。同理继续看下一个未被标记的点C,继续计算权重数组D+CD距离 2+1=3 <∞,拿C点也要修改权重为3,同时更新C点前驱是D,继续看下一个未被标记点E,发现权重数组D+DE=9<∞,所以也修改E点权重为9,同步更新它的前驱是D。如下表就是第一次更新后的结果
节点 | A | B | C | D | E |
---|---|---|---|---|---|
下标 | 0 | 1 | 2 | 3 | 4 |
权重 | 0 | 3 | 3 | 2 | 9 |
标记 | 1 | 0 | 0 | 1 | 0 |
前驱 | -1 | 3 | 3 | 0 | 3 |
2.找出当前表中未标记点权重最小的,来到了B点和C点,我们选择B(随便哪一个都行)将B标记为1,接着逐个判断未被标记点,首先是C,权重数组B+BC距离 3+4 >3所以不修改。又来到了E,权重数组B+BE 3+∞ >9 ,也不修改,如下表就是第二次更新的结果
节点 | A | B | C | D | E |
---|---|---|---|---|---|
下标 | 0 | 1 | 2 | 3 | 4 |
权重 | 0 | 3 | 3 | 2 | 9 |
标记 | 1 | 1 | 0 | 1 | 0 |
前驱 | -1 | 3 | 3 | 0 | 3 |
3.找出当前表中未被标价点权重最小的是C点,将C点标记为1,然后判断未被标记的,只剩E点,权重数组C+CE距离 3+3<9 所以修改E点权重是6,更行E点前驱是C的下标,如下表就是第三次更新的结果
节点 | A | B | C | D | E |
---|---|---|---|---|---|
下标 | 0 | 1 | 2 | 3 | 4 |
权重 | 0 | 3 | 3 | 2 | 6 |
标记 | 1 | 1 | 1 | 1 | 0 |
前驱 | -1 | 3 | 3 | 0 | 2 |
4.找出最小,只剩下E点,直接标记E点是1,结束。
节点 | A | B | C | D | E |
---|---|---|---|---|---|
下标 | 0 | 1 | 2 | 3 | 4 |
权重 | 0 | 3 | 3 | 2 | 6 |
标记 | 1 | 1 | 1 | 1 | 1 |
前驱 | -1 | 3 | 3 | 0 | 2 |
有了上面一张表,我们就可以求出每个点到A点的最短路径了,我举两个点例子。
首先是点B,根据前驱下标进行寻找,B前驱下标是3,对应点D,所以B点前驱是D,接着D点前驱下标是0,对应点A,接着A点前驱下标是-1,退出。所以整个逆序路径就是B-D-A
接着看E点,前驱下标是2就是C点,C点前驱是3是D点,D点前驱下标是0,是A点,A点前驱是-1,退出,整个逆序路径是E-C-D-A
//迪杰斯特拉算法
void Dijkstra(struct MGraph *g, char obj)
{
int *temp, *dis, *pre,index,min,k;
temp = (int*)malloc(sizeof(int) * g->numVretexes);
dis = (in