在下边的学习中,主要是学习指定一个点(源点)到其余的各个顶点的最短路径,也叫做“单源最短路径”。

例如下图中的1号顶点到2,3,4,5,6顶点的最短路径:

在这里要和flody算法一样,在这儿也需要用二维数组e来存取顶点之间和边之间的关系,数值如下:

在这儿我们还需要用一个一维数组dis来存取1号顶点到各个顶点的初始路程,如下:

因为dis中存取的是各个顶点到1号顶点的初始距离,所以我们把dis数组中的值称为最短路的“估计值”。

既然我们是求1号顶点到其余各个顶点的最短路程,那我们就可以先找一个距离1号顶点比较近的顶点。

我们通过看dis数组可知距离1号最近的是2号顶点。当我们选择了2号顶点之后,dis[2] 中的值由一个“估计值”变成了一个“确定值”,即1号顶点到2号顶点的最短路程就变成了当前的dis[2]的值。

当我们选定了2号顶点,我们接下来看2号顶点有哪些出边,有2-->3和2-->4这两条边,我们可以通过讨论2-->3这条边是否让1号到3号顶点的路程,也就是说现在我们来比较dis[3] 和 dis[2] + e[2][3] 的大小,其中dis[3] 表示1号顶点到3号顶点的路程:dis[2]
+e[2][3]中dis[2]表示1号顶点到2号顶点的路程, e[2][3]表示2-->3这条边。所以dis[2] + dis[2][3] 就表示从1号顶点到2号顶点,再通过2-->3这条边,到达3号顶点的路程。

dis[3] = 12 , dis[2] + e[2][3] = 1 + 9 = 10   ,dis[3] > dis[2] + e[2][3] ,因此dis[3] 要更新为10,这个过程我们成为“松弛”,1号顶点到3号顶点的路程即dis[3] , 通过2-->3这条边松弛成功。

这就是dijkstra的主要思想:通过“边”来松弛1号顶点到其余各个顶点的路程。

同理,我们可以通过2-->4(e[2][4]),我们可以将dis[4]的值从∞ 松弛为4(dis[4]初始为∞,dis[2] + e[2][4] = 1 + 3 = 4 , dis[4] > dis[2] + e[2][4] , 因此dis[4]的更新为4)

我们对2号顶点松弛之后的dis数组为:

我们对4号顶点的所有边(4 -- > 3 , 4 --> 5 和 4-->6)还用刚才的方法进行松弛,松弛之后为:

接下来对3号顶点的所有出边(3-->5)进行松弛,松弛之后的dis数组为:

对5号顶点的所有出边(5 --> 4)进行松弛,松弛完毕的dis数组为:

最后对6号顶点的所有出边进行松弛,得到最终的dis数组,这就是1号到所有点的最短路径:

我们来对上边的算法来进行一个总结,上述的算法的思想是:每次找到离源点(上述例子的源点就是1号顶点)最近的一个点,然后以该顶点为中心进行扩展,最终得到源点到其他点的最短路径。

完整的dijkstra的算法代码如下:

#include<iostream>
#include<cstdio>
using namespace std;
const int INF = 0x3f3f3f3f;
const int MAXN = 1009;
int e[MAXN][MAXN];int main()
{int dis[MAXN] , book[MAXN] ;int n , m , t1 , t2 , t3 , u , v , mi;while(cin >> n >> m)   //n代表点的个数,m代表边的条数{for(int i = 1 ; i <= n ; i ++)   //初始化{for(int j = 1 ; j<= n ; j ++){if(i == j)e[i][j] = 0;elsee[i][j] = INF;}}for(int i = 1 ; i <= m ; i ++){cin >> t1 >> t2 >> t3;e[t1][t2] = t3;}for(int i = 1 ; i <= n ; i ++) //初始化dis数组,这里是1号到其余各个顶点的初始路程{dis[i] = e[1][i];}//book 数组的初始化for(int i = 1 ; i <= n ; i ++)book[i] = 0;book[1] = 0;//dijkstra算法的核心语句for(int i = 1 ; i <= n ; i ++){//找到距离1号顶点最近的点mi = INF;for(int j = 1 ; j <= n ; j ++){if(book[j] == 0 && dis[j] < mi){mi = dis[j];u = j;}}book[u] = 1;for(int v = 1 ; v <= n ; v ++){if(e[u][v] < INF){if(dis[v] > dis[u] + e[u][v])dis[v] = dis[u] + e[u][v];}}}//输出最终的结果for(int i = 1 ; i <= n ; i ++){cout<<dis[i]<<" ";}}return 0 ;
}
/*
6 9
1 2 1
1 3 12
2 3 9
2 4 3
3 5 5
4 3 4
4 5 13
4 6 15
5 6 40 1 8 4 13 17
*/

通过上述代码可以看出来,这个算法的时间复杂度是O(N²)。其中每一次找到离1号顶点最近的点的时间复杂度是O(N),我们可以对这一算法进行优化,使这一部分的时间复杂度降低到O(logN)。

另外对于边数M少于N²的稀疏图来说(我们把M远小于N²的图称为稀疏图,而M相对较大的图我们称为稠密图),我们可以用邻接表来代替邻接矩阵,使得整个代码时间复杂度优化到O(M+N)log N 。 这种情况下最坏的情况就是M就是N²,这样的话(M+N)log N要比N²还要大,但是大多数情况下并不会有这么条边。。因此(M+N)log N要比N²小的多。

我们来说一下如何用邻接表来存图,我们可以先看数据:

4 5
1 4 9
2 4 6
1 2 5
4 3 8
1 3 7

第一行的两个数是n,m。n表示顶点的个数(顶点编号为1~n),m表示边的条数。

接下来的m行,每行都有三个整个t1,t2,t3.表示顶点t1到顶点t2的权值为t3。

我们用邻接表来存储这个图,代码如下:

int n , m ;
int u[1009] , v[1009] , w[1009];
int first[1005] , next[1005];
cin>>n>>m;
//初始化first数组下标1~n的值为-1,表示1~n顶点暂时都没有边
for(int i = 1 ; i <= n ; i ++)
{first[i] = -1;
}
for(int i = 1 ; i <= m ; i ++)
{cin >> u[i] >> v[i] >> w[i];  //输入每一条边next[i] = first[u[i]];//关键语句first[u[i]] = i ;
}

在这里说的是使用该数组来实现邻接表,没有真正使用指针链表,这是一种在实际应用中非常容易实现的方法,这种方法为每个顶点 i(i从1~n)都设置一个链表,里面保存了从顶点 i 出发的所有的边(这里用first 和 next 数组来实现)

首先我们需要为每一条边进行1~m的编号。用u,v和w 三个数组来记录你每条边的信息,即u[i] , v[i] 和 w[i]表示的是第 i 条边是从u[i] 号顶点到v[i] 号顶点(u[i] --> v[i]),且权值为w[i]。

first 数组的1 ~ n号元素单元格分别用来存储 1 ~ n 号顶点的第一条边的编号,初始的时候因为没有边加入,所以都是-1。即first[u[i]] 保存顶点u[i]的第一条边的编号,next[i] 存储的是 ”编号是i的边“ 的” 下一条边“的编号。

接下来我们看一下存入一条边之后的每一个数组的元素的值。

接下来我们要考虑的就是我们应该怎么遍历每一条边。

上边说的其实first数组存的是每个顶点 i (i 从1~n)的第一条边。比如1号顶点的第一条边是编号为5的边(1,5,7),2号顶点的第一条边是编号为 4 的边(2,4,6) , 3 号顶点没有出向边, 4 号顶点的第一条边的编号为2的边(2 4 6),那么我们如何遍历1号顶点的每一条边呢?看下图:

在找到1号顶点的第一条边后,剩下的边都可在next数组中依次找到。看代码:

k =  first[1];
while(k != -1)
{cout<<u[k]<<v[k]<<w[k]<<endl;k = next[k];
}

我们仔细看可以发现遍历某个顶点的边的时候的遍历顺序正好与读入时候的顺序相反。

因为我们在为每一个顶点插入边的时候都是直接插入“链表” 的首部,而不是尾部。不过这样并不会有什么问题,这正是这种方法的奇妙之处。我们来遍历每个顶点的边,其代码如下:

for(int i = 1 ; i <= n ; i ++)
{k = first[i];while(k != -1){cout<< u[k] << v[k] << w[k] << endl;k = next[k];}
}

我们可以发现使用邻接表来存储图的时间空间复杂度是O(M),遍历每一条边的时间复杂度也是O(M)。如果是稀疏图的话,M要远小于N²。因此稀疏图选用邻接表来存储要比用临界矩阵存储好的多。

Dijkstra算法讲解(通过边实现松弛)相关推荐

  1. Dijkstra算法求解单源最短路径问题

    文章目录 一 前言 二 Dijkstra 算法讲解 1. 贪心算法的证明 2. 算法实现说明 3. 初版Dijkstra算法代码 三 时间复杂度优化 1. 优化策略 2. 优化后的代码 四 结语 一 ...

  2. (Dijkstra算法)设计实现一个全国大城市间的交通咨询程序,为旅客提供三种最优决策方案:(1)飞行时间最短(2)费用最小(3)中转次数最少。

    设计实现一个全国大城市间的交通咨询程序,为旅客提供三种最优决策方案: (1)飞行时间最短 (2)费用最小 (3)中转次数最少 数据如下: 机  号 出 发 地 到 达 地 出发时间 到达时间 费  用 ...

  3. [C] Dijkstra算法——通过边实现松弛

    Dijkstra算法--通过边实现松弛 本算法学习指定一个点(源点)到其余各个顶点的最短路径,也叫做单源最短路径例如求下图1号顶点到2,3,4,5,6号顶点的最短路径 这个时候你可能就要问了,为什么不 ...

  4. 【转载】最短路径之Dijkstra算法详细讲解

    1  最短路径算法 在日常生活中,我们如果需要常常往返A地区和B地区之间,我们最希望知道的可能是从A地区到B地区间的众多路径中,那一条路径的路途最短.最短路径问题是图论研究中的一个经典算法问题, 旨在 ...

  5. Dijkstra算法 详细讲解

    Dijkstra算法 详细解释 Dijkstra算法适用于边权值为正的情况,如果边权值为负数就才用另一种最短路算法Bellman-Ford算法. 该算法是指从单个源点到各个结点的最短路,该算法适用于有 ...

  6. 图论算法讲解--最短路--Dijkstra算法

    一.绪论 要学习最短路算法我们首先应该知道什么是图以及什么是最短路. 图在离散数学中的定义为:图G=(V,E)是一个二元组(V,E)使得E⊆[V]的平方,所以E的元素是V的2-元子集.为了避免符号上的 ...

  7. floyed算法、dijkstra算法、SPFA算法

    转自gzr的博客:https://www.cnblogs.com/TFLS-gzr/p/10381849.html,https://www.cnblogs.com/TFLS-gzr/p/1038746 ...

  8. 最短路算法 :Bellman-ford算法 Dijkstra算法 floyd算法 SPFA算法 详解

     本文链接   :http://www.cnblogs.com/Yan-C/p/3916281.html . 在本文中因为邻接表在比赛中不如前向星好写,而且前向星效率并不低所以,本文的代码 存图只 ...

  9. Dijkstra算法入门

    前言 最短路径问题(Shortest Path Problem)是一类非常重要的问题,它出现在很多领域,例如车辆导航.路由选择.机器人运动规划.物流等.Dijkstra 算法是一种解决最短路径问题的经 ...

最新文章

  1. 【团队】 冲刺一(10/10)
  2. 批处理修改远程桌面端口,并修改相应的防火墙配置
  3. linux 杀软 clamav 帮助选项翻译
  4. 中原银行张本晨:中原银行数字化营销体系建设实践
  5. oracle 0值处理判断,ORACLE数据统计0的处理
  6. .NET实现之(部署ActiveX控件—简)
  7. 一个切图仔的 CSS 笔记
  8. Oracle备份与恢复 expdp/impdp数据泵远程导入导出
  9. 学习编程的条件是什么?
  10. (初学者)初学者的编程的苦恼
  11. python 参数带星号_python 函数参数的传递(参数带星号的说明)
  12. oracle国家字符集
  13. Ubuntu+ros 绑定端口,一劳永逸,再也不怕端口变化啦!
  14. Pandas query 的用法, df.query
  15. NC:恢复菌群多样性或能降低耐药性
  16. Office三件套批量转PDF以及PDF书签读写与加水印
  17. js正则表达式匹配纯中文或纯英文
  18. 使用python itchat模块实现微信聊天机器人_code
  19. ps快捷键对应的英文字母缩写
  20. 计算机应用Office2016,Office 2016

热门文章

  1. jdk+apache+jboss+mod_jk+openssl--从零开始搭建Linux测试环境
  2. 【JAVA SE】 JAVA基础强袭之路 数据类型及其转换和提升全面讲解(猛男细节+保底一个收藏)
  3. 2021最新全球CS专业排名发布,CMU再夺榜首,清华和MIT并列第三!
  4. 移动APP、WEB端、PC端 测试区别
  5. 恢复微信撤回的图片(不容root,极其简单)
  6. LiveNVR传统安防摄像机互联网直播-二次开发相关的API接口
  7. 微信地图wgs84坐标,gcj02坐标,bd09坐标转换
  8. 不如跳舞:AI自动合成舞蹈视频
  9. 【Python】turtle海龟画图练习
  10. OpenCV+MFC 显示图像