最短路径算法(迪杰斯特拉算法,弗洛伊德算法)

最短路径:

非网图:指两个顶点之间经过的边的数量最少的路径

网图:指两个顶点之间经过的边上权值之和最少的路径

两种算法:

  1. 迪杰斯特拉算法:求某个源点到其余各顶点的最短路径问题
  2. 弗洛伊德算法:求图中每一对顶点之间的最短路径

1.迪杰斯特拉算法(Dijkstra)

迪杰斯特拉是一个按路径长度递增的次序产生最短路径的算法

大致道理:

引进辅助分量D,每个分量D[i]表示从初始点V_{0}到终点V_{i}的最短路径的长度。

辅助分量的初始状态为:若从V_{0}V_{i}有弧,则D[i]为弧上的权值,若没有弧,则置D[i]为无穷大

D[j]=Min{D[i] |V_{i}\in V},则最短路径就是从初始点V_{0}到终点V_{j}的这条弧,为(V_{0},V_{j})

那么长度次短的最短路径是哪条呢?

假设次短路径的终点为V_{k},那么这条路径有2种情况:

可能为(V_{0}V_{k}):即初始点V_{0}和终点V_{k}存在直接相连的弧,长度为D[k]

可能为(V_{0}, V_{j}V_{k}):即通过V_{j}V_{0}V_{k}间接相连,长度为D[j]与从V_{j}V_{k}弧上的权值之和


假设用带权的邻接矩阵arcs来表示带权有向图,arcs[i][j]表示弧<V_{i}V_{k}>的权值,若不存在弧<V_{i}V_{k}>,则置值为无穷

S为已经找到从V_{0}出发最短路径的终点的集合,初始状态为空集.S={}

算法步骤:

  1. D[0]=0,D[i]=arcs[0][i];
  2. D[j]=Min{ D[i] | V_{i}\in V-S}
  3. S= S\bigcup \left \{j \right \}
  4. 修改从 V_{0}出发到集合V-S上任一顶点V_{k}可达的最短路径,如果D[j]+arcs[j][k]
  5. 重复2~3共n-1次,求得V_{0}到剩余n-1个顶点的最短路径

代码展示:

void ShortestPath_DIJ(MGraph G,int v0, Patharc *P,ShortPathTable *D)
{    
    int final[MAXVEX];
    D[0]=0;final[0]=TRUE;//V0到自己的路径权值为0,令final[0]=TRUE,初始化将V0加入S
    for(int i=1;i

算法距离:

最短路径算法(迪杰斯特拉算法,弗洛伊德算法)_第1张图片最短路径算法(迪杰斯特拉算法,弗洛伊德算法)_第2张图片


2.弗洛伊德算法(Floyd):

目的:弗洛伊德算法是用于求网中任意一个顶点到任意另外一个顶点的最短路径以及最短路径代价。

为实现从任意顶点到其余所有顶点的最短路径,此时简单的算法可以是对每个顶点作为源点运行一次迪杰斯特拉算法O(n^2),等于在原来算法的基础上再来一层循环,整个算法的时间复杂度为O(n^3)

前言:弗洛伊德的《《梦的解析》》是一本心理学著作,不清楚是否是同一个人。本人学识浅薄,弗洛伊德算法虽然稍微晦涩难懂,但是它是我目前的见过的最美妙的算法,充分体现的人类思维的创造性和算法的美妙性。

思路:寻找最短路径的算法一般而言都是一步步探索,在前面的路径探索基础上得出最短的路径求解。别说计算机,就算连人这种智能生物也需要逐步的摸索才能找到一条最短的路。比如说不给你地图,让你自己去摸索从杭州到北京花钱最少的交通方式,或者耗时最少的交通方式,你也需要走一步看一步的考虑。

我们先来看一个简单的案例---最简单的3个顶点的连通网图,由浅到深看一看弗洛伊德算法。

最短路径算法(迪杰斯特拉算法,弗洛伊德算法)_第3张图片

我们定义的2个二维数组,维度为顶点数:


D[3][3]:顶点到顶点的最短路径权值和的矩阵。对于无向网来说是一个对称矩阵,对有向网则不一定是对称矩阵。

比如说:D[0][2]代表顶点V0到顶点V2的最短路径上权值之和

对无向网来说,从顶点Vi到顶点Vj的最短路径也是从顶点Vj到顶点Vi的最短路径,故矩阵对称

对有向网来说,从顶点Vi到顶点Vj存在最短路径,不代表从顶点Vj到Vi存在最短路径

在未分析任何顶点前,将D命名为D^{-1},其实它就是图的邻接矩阵,如上图


P[3][3]:代表对应顶点之间最小路径的前驱顶点矩阵(这样说就太抽象了,是我的话也看不懂)

举个例子:

假设P[1][2]=0,说明V0在从顶点V1到顶点V2的最短路径上,这条路径是从V1到V0,然后去查找从V0到V2的最短路径。也就是从V1出发,下一个落脚点是V0,至于下一个落脚点,我们的去看看从V0到V2的最短路径前驱顶点即P[0][2]即为下个落脚点。

在未进行分析之前,我们将P命名为P^{-1},此时P^{-1}\left [ i \right ]\left [ j \right ]=j,如上图

如何理解呢?初始化时,无论两个顶点是否存在最短路径,没有相连的话假设路径权值之和为无穷大就好了。顶点Vi到Vj的最短路径是直接从Vi到Vj端到端,没有中间商赚差价,从Vi出发的下一个落脚点就是终点Vj。


我们从V0开始,分析所有顶点经过V0后到达另一顶点的最短路径。

因为只有3个顶点,所以查看V1——>V0——>V2,得D^{-1}\left [ 1 \right ]\left [ 0 \right ]+D^{-1}\left [ 0\right ]\left [ 2 \right ]=3<D^{-1}\left [ 1 \right ]\left [ 2 \right ]=5

说明V1——>V0——>V2比直接V1——>V2距离还近:

D^{-1}\left [ 1 \right ]\left [ 2 \right ]=3,因为对称,所以D^{-1}\left [ 2 \right ]\left [ 1 \right ]=3,于是得到新的D^{0}矩阵,如上图

修改P^{-1}\left [ 1\right ]\left [ 2\right ]=0,因为对称,所以P^{-1}\left [ 2\right ]\left [ 1\right ]=0,由V1到V2的中间顶点为V0,于是得到新的P^{0}矩阵,如上图

即 :D^{0}[v][w]=min\left \{ D^{-1}[v][w] , D^{-1}[v][0]+D^{-1}[0][w]\right \}

于是,接下来在D^{0}P^{0}的基础上继续处理所有顶点经过V1后到达另一顶点的最短路径,得到D^{1}P^{1}

接下来在D^{1}P^{1}的基础上继续处理所有顶点经过V2后到达另一顶点的最短路径,得到D^{2}P^{2},得到结果。


接下来我们用复杂的网图来讲解弗洛伊德算法,如下图所示:

最短路径算法(迪杰斯特拉算法,弗洛伊德算法)_第4张图片

 来人!!上代码!!

typedef int Pathmatrix[MAXVEX][MAXVEX]
typedef int ShortPathTable[MAXVEX][MAXVEX]
void ShortestPath_Floyd(MGraph G,Pathmatrix *P,ShortPathTable *D)
{   //初始化矩阵D和P
    for(int v=0;v(*D)[v][k]+(*D)[k][w] )
                {
                    (*D)[v][w]=(*D)[v][k]+(*D)[k][w];//如果经过顶点k距离更小,则更新权值
                    (*P)[v][w]=(*P)[v][k];//将由v到k的最短路径纳入v到w的最短路径
                }
            }
        }
    }
    return;
}//这代码是真的简洁优美

打印最短路径的代码为:

for(int v=0;vV%d weight:%d",v,w,D[v][w]);//打印源点和终点以及路径权值和
        int k=P[v][w];//获取第一个路径顶点下标
        printf(" Path:%d",v);//打印源点
        while(k!=w){
            printf("-->%d",k);//打印路径中间顶点
            k=P[k][w];//寻找到下一个路径终点顶点
        }
        printf("-->%d\n",w);//打印终点
    }
    printf("\n");
}

多么漂亮的算法啊!就是一个简单优美的三重循环!时间复杂度也是O(n^3),当需要求所有顶点到其他所有顶点的最短路径时,弗洛伊德算法是一个不错的算法!

参考:大话数据结构

你可能感兴趣的:(C语言学习,数据结构与算法)