最短路径

在生活中,例如,当我们坐公交或轻轨时,都会看一下交通图,找到在哪个站下是最快能达到目的地的,也就是路径最小。考虑到交通图的有向性,例如汽车的上山下山、轮船的顺水逆水,所花费的时间或代价就不相同,所以交通网往往是用带权的有向网表示。在带权的有向网中,习惯上称路径上的第一个顶点称为源点(Source),最后一个顶点称为终点(Destination)

下面介绍两种最常见的最短路径问题:

  1. 从某个源点到其余各顶点 的最短路径
  2. 每一对顶点之间的最短路径

源点 → \rightarrow →其余各顶点(迪杰斯特拉算法)

迪杰斯特拉(Dijkstra)提出了一个按路径长度递增的次序产生最短路径 的算法。

1. 求解过程

对于网 N = ( V , E ) N=(V,E) N=(V,E),将N中的顶点分为两组(同MST性质一样):
S:已求出最短路径所包含的顶点
V-S:未求出的最短路径的顶点

该求解过程也就是按各顶点与 v 0 v_{0} v0​间最短路径长度递增的次序,逐个将V-S中的顶点加入到S中

例如,对于下图求解其 v 0 v_{0} v0​的最小路径

根据迪杰斯特拉算法的求解过程,首先求出 v 0 v_{0} v0​到 v 2 v_{2} v2​的路径 < v 0 , v 2 > <v_{0},v_{2}> <v0​,v2​>,然后按路径长度递增的次序依次得到 v 0 v_{0} v0​到 v 4 v_{4} v4​的路径 < v 0 , v 4 > <v_{0},v_{4}> <v0​,v4​>, v 0 v_{0} v0​到 v 3 v_{3} v3​的路径 < v 0 , v 4 , v 3 > <v_{0},v_{4},v_{3}> <v0​,v4​,v3​>, v 0 v_{0} v0​到 v 5 v_{5} v5​的路径 < v 0 , v 4 , v 3 , v 5 > <v_{0},v_{4},v_{3},v_{5}> <v0​,v4​,v3​,v5​>,而 v 0 v_{0} v0​到 v 1 v_{1} v1​没有路径。

或许用下面的表格来表示更加清晰:

源点 终点 最短路径 路径长度
v 0 v_{0} v0​ v 2 v_{2} v2​ < v 0 , v 2 > <v_{0},v_{2}> <v0​,v2​> 10
v 0 v_{0} v0​ v 4 v_{4} v4​ < v 0 , v 4 > <v_{0},v_{4}> <v0​,v4​> 30
v 0 v_{0} v0​ v 3 v_{3} v3​ < v 0 , v 4 , v 3 > <v_{0},v_{4},v_{3}> <v0​,v4​,v3​> 50
v 0 v_{0} v0​ v 5 v_{5} v5​ < v 0 , v 4 , v 3 , v 5 > <v_{0},v_{4},v_{3},v_{5}> <v0​,v4​,v3​,v5​> 60
v 0 v_{0} v0​ v 1 v_{1} v1​ ∞ \infty ∞

熟悉了该算法的求解过程后,下面来实现该算法的代码

2. 算法实现

算法分析:
在这里我们对于无向网仍用邻接矩阵的存储表示,源点为 v 0 v_{0} v0​。

  1. 首先,我们想到的是最短路径一定有一个值,那么我们可以用一维数组来存储当前 v 0 v_{0} v0​到其余各顶点的最短路径的值,命名该数组为 D [ i ] D[i] D[i]。若就 v 0 v_{0} v0​和 v i v_{i} vi​之间没有弧,则 D [ i ] = ∞ D[i]=\infty D[i]=∞。!!注意:该数组的初始化也被赋于就是 v 0 v_{0} v0​和 v i v_{i} vi​之间的弧的权值,且默认该值就是当前的最小路径,对于后续操作,我们会对该数组中的值进行更新,通过比较来取得更小的路径。
  2. 其次,我们对于这些算法大都有循环结构,那么我们就需要直到 v 0 v_{0} v0​到 v i v_{i} vi​的最短路径是否已被确定。我们对此也可以用一个一维数组 S [ i ] S[i] S[i]来记录,若已确定,则 S [ i ] = t u r e S[i]=ture S[i]=ture,否则 S [ i ] = f a l s e S[i]=false S[i]=false。
  3. 那么还有一个问题需要我们考虑的是,若遇到上图 v 0 v_{0} v0​到 v 1 v_{1} v1​没有路径的情况该怎么办。我们需要记录其余顶点的直接前趋是否存在,也可以用一个一维数组 P a t h [ i ] Path[i] Path[i]来存储该顶点的直接前趋的顶点序号。例如,若 v 0 v_{0} v0​到 v i v_{i} vi​若有弧,则 P a t h [ i ] = v 0 Path[i]=v_{0} Path[i]=v0​,否则 P a t h [ i ] = − 1 Path[i]=-1 Path[i]=−1。
  4. 每当加入一个新的顶点到S中,则该顶点可作为一个“中转站”。例如,对于上述无向网,假如此时S中已有 v 0 v_{0} v0​和 v 2 v_{2} v2​,若要求 v 0 v_{0} v0​到 v 3 v_{3} v3​的最小路径,那么首先我们已有 v 0 v_{0} v0​直接到 v 3 v_{3} v3​的路径(也就是只有一条弧的路径) D [ 3 ] D[3] D[3],然后再将 v 0 → v 2 → v 3 v_{0}\rightarrow v_{2}\rightarrow v_{3} v0​→v2​→v3​的路径 D [ 2 ] + a r c s [ 2 ] [ 3 ] D[2]+arcs[2][3] D[2]+arcs[2][3]与 D [ 3 ] D[3] D[3]比较,若中转后的路径比原来的路径小,则将中转后的路径替换原来的 D [ 3 ] D[3] D[3]。
  5. 直到所有顶点都加入到S中为止。

算法步骤:

  1. 首先是各数组的初始化,从源点 v 0 v_{0} v0​出发,将 v 0 v_{0} v0​加入到S中,并使 S [ v 0 ] = t r u e S[v_{0}]=true S[v0​]=true
  2. 对 v 0 v_{0} v0​到其余各顶点的最短路径长度初始化为权值,即 D [ i ] = G . a r c s [ v 0 ] [ v i ] D[i]=G.arcs[v_{0}][v_{i}] D[i]=G.arcs[v0​][vi​]
  3. 再判断 v 0 v_{0} v0​和 v i v_{i} vi​之间是否有弧,若有,则将 v i v_{i} vi​的前趋置为 v 0 v_{0} v0​,即 P a t h [ i ] = v 0 Path[i]=v_{0} Path[i]=v0​,否则 P a t h [ i ] = − 1 Path[i]=-1 Path[i]=−1。
  4. 初始化结束之后,我们就该用循环结构来寻求最短路径(循环n-1次):
  • 选择下一条最短路径的终点 v k v_{k} vk​,将 v k v_{k} vk​加入到S中,并且使 S [ v k ] = t r u e S[v_{k}]=true S[vk​]=true
  • 根据条件更新从 v 0 v_{0} v0​出发到剩下的任一顶点(V-S中的顶点)的最短路径,若 D [ k ] + G a r c s [ k ] [ i ] < D [ i ] D[k]+Garcs[k][i]<D[i] D[k]+Garcs[k][i]<D[i]成立,则更新 D [ i ] = D [ k ] + G . a r c s [ k ] [ i ] D[i]=D[k]+G.arcs[k][i] D[i]=D[k]+G.arcs[k][i],同时更改 v i v_{i} vi​的前趋为 v k v_{k} vk​,即 P a t h [ i ] = k Path[i]=k Path[i]=k

有了前面的分析和步骤,下面给出具体的代码:

3. 具体代码

这里以上述的有向网G6为例(默认 v 0 → v 5 v_{0}\rightarrow v_{5} v0​→v5​的下标为0~5):

#define MaxInt 32767void ShortestPath_DIJ(AMGraph G,int v0)
{int v=0;//初始化for(int v=0;v<G.vexnum;v++){S[v]=false;D[v]=G.arcs[v0][v];if(D[v]<MaxInt)Path[v]=v0;elsePath[v]=-1;     }S[v0]=true;D[v0]=0;//开始求最短路径for(int i=0;i<G.vexnum;i++){min=MaxInt;for(int w=0;w<G.vexnum;w++)                          //找出当前的最短路径if(!S[w]&&D[w]<min){v=w;min=D[w];}S[v]=true;for(int w=0;w<G.vexnum;w++)                          //更新D[w]if(!S[w]&&(D[v]+G.arcs[v][w]<D[w])){D[w]=G.arcs[v][w];Path[w]=v;}}
}

简单概括一下:首先我们从D[i]中选出最短的路径,将该顶点加入到S中,此时S中有 v 0 , v 2 v_{0},v_{2} v0​,v2​(假设新加入的顶点是 v 2 v_{2} v2​),然后进行D[i]更新,此时 v 2 v_{2} v2​作为中转站,若经过 v 2 v_{2} v2​再到 v 3 v_{3} v3​(剩余顶点中的一个,这里只是举一个具体的例子,全部顶点都要进行比较)的路径比 v 0 v_{0} v0​直接到 v 3 v_{3} v3​的路径短,则更新D[3]。然后又从更新后的D[i]中选出路径最小的,又将该顶点(假定是 v 4 v_{4} v4​)加入S中,此时S中有 v 0 , v 2 , v 4 v_{0},v_{2},v_{4} v0​,v2​,v4​,然后又更新D[i],此时 v 4 v_{4} v4​作为中转站(注意,此时D[i]中的值可能已经在 v 2 v_{2} v2​中转过),假如 v 3 v_{3} v3​已在 v 2 v_{2} v2​中转过,那么此时D[3]的值就为路径 v 0 → v 2 → v 3 v_{0}\rightarrow v_{2}\rightarrow v_{3} v0​→v2​→v3​的权值和,若再在 v 4 v_{4} v4​中转后到 v 3 v_{3} v3​的路径(即 v 0 → v 2 → v 4 → v 3 v_{0}\rightarrow v_{2}\rightarrow v_{4}\rightarrow v_{3} v0​→v2​→v4​→v3​)比D[3]小,那么就对其进行更新…

顶点 → \rightarrow →顶点(弗洛伊德算法)

求解各顶点间的最短路径在前面的基础上就比较简单了,我们可以调用n次迪杰斯特拉算法求得n个顶点分别到其余顶点的最短路径。但这个方法形式上比较复杂。下面介绍 弗洛伊德(Floyd) 算法来解决这一问题,两种算法的时间复杂度都为 O ( n 3 ) O(n^3) O(n3),但弗洛伊德算法在形式上更为简单,更容易理解。

1. 算法实现

我们仍用带权的邻接矩阵来表示有向网,同时引入一下辅助数组:

  1. P a t h [ i ] [ j ] : Path[i][j]: Path[i][j]:最短路径上顶点 v j v_{j} vj​的前一顶点的序号。
  2. D [ i ] [ j ] : D[i][j]: D[i][j]:记录顶点 v i v_{i} vi​和 v j v_{j} vj​之间的最短路径长度。

算法分析:

  1. 该算法实际上十分容易理解,第一步还是先对最短路径长度数组和前趋数组进行初始化
  2. 初始化完成后,就是比较并更新了,对于两个不相同的顶点 v i v_{i} vi​和 v j v_{j} vj​,若在它们之间路径增加一个顶点 v k v_{k} vk​,若 v i → v k v_{i}\rightarrow v_{k} vi​→vk​加上 v k → v j v_{k}\rightarrow v_{j} vk​→vj​的路径小于 v i → v j v_{i}\rightarrow v_{j} vi​→vj​的路径,则对 D [ i ] [ j ] D[i][j] D[i][j]进行更新,并更新 P a t h [ i ] [ j ] = P a t h [ k ] [ j ] Path[i][j]=Path[k][j] Path[i][j]=Path[k][j]。当在 v i v_{i} vi​和 v j v_{j} vj​已试过增加其余各个顶点后,那么就改变 j j j的值,再进行一轮循环增加中间顶点。当 j j j已全部试过后,就开始改变 i i i的值,综上,也就是说这是一个循环含有一个内循环和一个内循环的内循环,也就是三重循环。

2. 具体代码

void ShortestPath_Floyd(AMGraph G)
{//初始化for(int i=0;i<G.vexnum;i++)for(int j=0;i<G.vexnum;j++){D[i][j]=G.arcs[i][j];if(D[i][j]<MaxInt&&i!=j)         //两部相同顶点之间有弧Path[i][j]=i;else Path[i][j]=-1;}//更新D[i][j]数组for(int k=0;k<G.vexnum;k++)for(int i=0;i<G.vexnum;i++)for(int j=0;j<G.vexnum;j++)if(D[i][k]+D[k][j]<D[i][j]){D[i][j]=D[i][k]+D[k][j];Path[i][j]=Path[k][j];        //j的前趋变成k}
}

总结

关于最短路径的两种算法,各自都有方便之处,假设我们在玩王者荣耀,每次复活后,从水晶出发,都想以最快的速度到达敌方的任一一座防御塔,那么此时我们就可以用迪杰斯特拉算法来实现这个想法。如果我们在不死亡的情况,可以出现在地图任一一座防御塔旁,此时我们需要最快地到达另一座防御塔,此时也可以用迪杰斯特拉算法,但弗洛伊德算法相较于迪杰斯特拉算法结构上更简单,更方便实现。

图——最短路径的两种算法相关推荐

  1. 最短路径的两种算法(迪杰斯特拉算法和弗洛伊德算法)

    一.迪杰斯特拉(Dijkstra)算法 1.定义描述 Dijkstra(迪杰斯特拉)算法是典型的单源最短路径算法,用于计算一个节点到其他所有节点的最短路径.主要特点是以起始点为中心向外层层扩展,直到扩 ...

  2. 图--最短路径(四种算法详解)

    最短路径 dijkstra算法 邻接矩阵实现 邻接表实现 最小堆优化 应用:多权值+多路径+路径输出 Floyd算法 Bellman ford算法 SPFA算法 dijkstra算法 单源最短路径算法 ...

  3. 【数据结构】什么是图的最短路径?实现最短路径的2种算法?

    目录 一.什么是最短路径? 二.实现最短路径的2种算法? 三.最短路径 1.某个顶点到其余各顶点的最短路径:迪 杰斯特拉(Dijkstra)算法 2.每一对顶点之间的最短路径: 佛洛依德(Floyed ...

  4. Matlab-TOA定位算法性能仿真 TOA-LLOP和TOA-CHAN得到位置的估计 两种算法的性能比较 两种算法的RMSE曲线对比图

    本博客环境为Matlab2018 ,软件版本不同可能会有些出入,需要稍作修改. TOA定位算法性能的仿真 要求一:编写两个函数TOA_LLOP和TOA_CHAN得到位置的估计. 要求二:用RMSE实现 ...

  5. rls lms 对比 matlab,自适应均衡器的LMS和RLS两种算法的特性与仿真分析

    自适应均衡属于自适应信号处理的应用范畴,各种各样的自适应均衡算法如迫零(ZF)算法.最小均方(LMS)算法.递归最小二乘(RLS)算法.变换域均衡算法.Bussgang算法.高阶或循环统计量算法.基于 ...

  6. 数字图像隐藏图像的两种算法及实现代码

    数字图像 二值图像 也叫单色图像,是将每个像素点存放在一个bit空间(值为0或者1)的图像,也就是说每个像素"非黑即白",主要用于图像形态学的研究. 8位灰度图像 每个像素存放在一 ...

  7. 欧拉回路/路径浅谈(七桥问题,两种算法)

    文章目录 前言 引子 欧拉回路/路径 定义 欧拉路径 欧拉回路 无向图(连通) 欧拉回路-无向 欧拉路径-无向 有向图(连通) 欧拉回路-有向 欧拉路径-有向 注意事项 算法 Fluery算法 Hie ...

  8. 深度优先搜索(DFS)和广度优先搜索(BFS)两种算法c++

    1.BFS和DFS介绍 深度优先搜索(DFS)和广度优先搜索(BFS)是一种用于遍历或搜索树图的一种算法,在这个过程中保证图或数的每个结点被访问且仅被访问一次,再按照每个结点访问的顺序不同分为深搜和广 ...

  9. 带你辨析最小生成树的两种算法

    最小生成树的算法通常由在几个城市中间修筑路径使其连通并且花费最小引出.如下图所示引例. 多数求最小生成树算法都应用了简称为MST的性质:假设N=(V,{E})是一个连通网,U是顶点V的一个非空子集.若 ...

最新文章

  1. 10624 - Super Number
  2. 图卷积神经网络分析复杂碳水化合物
  3. 全球超算500强榜单更新:美国Summit居首,中国上榜227台
  4. 马宁的Windows Phone 7开发教程(3)——XNA下使用MessageBox和软键盘
  5. 基于密钥的认证机制(ssh)
  6. Java构造方法以及重载
  7. Python笔记-UiSelector中resourceId定位方式
  8. Linux检查CPU过高的原因
  9. 数据-第6课-线性表的相关操作
  10. 网站服务器中病毒或被***怎么办?
  11. php网站程序更新功能,运用PHP定时自动更新网站首页HTML的方法
  12. html5通讯录模板,[应用模板]HTML5+Phonegap通讯录
  13. 模糊综合评价法及Python实现
  14. ps使用脚本生成fnt
  15. ear的英语怎么念_高中英语快速记忆法有哪些?
  16. python主题更改_jupyter notebook更换皮肤主题的实现
  17. C#实例练习3:程序流程控制(2)
  18. MFC调用Opencv显示视频
  19. 哪种耳机适合跑步用、跑步运动耳机推荐
  20. ZYNQ-7的芯片引脚

热门文章

  1. 2u服务器最多几个CPU,双插槽2U机架服务器适合需要高性能、存储容量
  2. Java输出PPT文件(一) - 合并PPT
  3. [转]较全的OA系统功能模块需求描述
  4. 基于 Winform + DotNetBar 写的股市行情助手
  5. 进入华为软件 eNSP 学习的第一天
  6. 1 - 什么是机器学习?怎么用?
  7. PS文字换行如何防止标点符号排一行第一个位置
  8. 生产齐套分析单/缺料分析单子项明细自定义字段-金蝶社区转载
  9. 【回顾从前】2012年思科认证考试等级划分
  10. linux磁盘分区fdisk命令详解及云硬盘挂载实操