前言:
最近在刷算法模板题的时候发现最短路卡掉了我很多脑细胞,所以特别冲浪了一下学习最短路算法这个好理解的算法。
算法简介:
Dijkstra就是个求单源最短路的算法,单源最短路,就是从一个起点出发到其他所有点的最短路。(与之相对的多源最短路,即能求任意两点之间的最短路)Dijkstra算法使用的思想是贪心,要知道贪心是最符合人类思维的算法,时间复杂度是O()的,使用堆优化的复杂度是O()的,非常高效。
Dijkstra的贪心思路是这样的:
为了方便起见,我们定义 点集S为已经确定最短路的点的集合,下图用红色点表示;点集U为更新过的,但是没有确定为最短路的点的集合,下图用蓝色点表示;点集V为还没有更新过的点的集合,下图用白色点表示。
1.从起点出发,扩展并更新它能连接的所有点的数值。以下图为样例,假设从1点出发。那么下图的模拟就是,先从1点出发,并且确定1是最短路的点,那么它就是已经确定最短路的点(因为自己到自己的最短路是0,什么?你问后面连接负数边权?废话,Dijkstra本来就不能处理负边权)。向外直接连接的点是2、3、4、7,经过1过程那么扩展成蓝色点。
2.从蓝点里找一个数值最小的点,以它为中继点执行像1.一样的操作,就是从上图的2点(数值是2)出发继续扩展,同时需要注意的是,2点这个时候可以确定是了它的数值就是到起点的最短路了,证明见后。模拟上图就成为了下图。因为2点直接连接的是1点和6点,而1点已经红了,那么只能更新没更新过的6点的数值,让6点蓝起来。
3.重复2.的步骤,找到蓝点里面数据最小的那个用它来遍历下一步。(上图为3点扩展,成为下图),由3点扩展5点,把3点变红,5点变蓝。
4.重复步骤,蓝点最小的是5点,从5点扩展,5点直接连接到1、4、7。1点红了在1.解释过,不在扩展。重点来了!4、7点都是蓝点怎么办。别急,5点连接到4、7,那么就意味着,1点到4和7的最短路可能会被更新(至于为什么证明在后面),那么从5点的数据到4点的数据更新吗?5->4边权是4,5数据是7。7+4=11 > 4点数据“7”,所以没必要更新。因为1->4的路径比1->3->5->4更好。同理5->7也是一样不需要更新。这个时候让5点红了就行。
5.继续扩展7点,无法更新6点.......继续更新4点,突然发现 7+1=8 < 10,所以更新6点
6. 6点无法更新,直接变红,算法over。就是说,从1到2..7的最短路分别是2、4、7、7、8、5。
我觉得我讲的挺详细的,如果还不明白,可以不用学了私信我。
至于这种算法为什么正确,且看下列证明。
开始的时候默认是起始点位于S点集 中,
从起始点扩展的点都加入U点集 中,那么显然可以看出U中连到S中,U中最小的那个连到S中的话,一定意味着这个点能到达起点是最短路。
证毕
下面给出伪代码:(连接矩阵存图)
w[i][j];//从点i到点j的边权
map[V];//第一个点到其他点的边权记录
memset(map,0x3f);//初始化任意两个点都最大
map[1]=0;//第一个点到自身的最短路一定是为0
for(int i=1;i<=n;i++){
int t=-1; // 在还未确定最短路的点中,寻找距离最小的点
for(int j=1;j<=n;j++)//从一号点开始
if(!vis[j] and (t==-1 or map[t]>map[j]))
t=j;
vis[t]=1;
// 用t更新其他点的距离
for(int j=1;j<=n;j++)
map[j]=min(map[j],map[t]+w[t][j]);
}
if(map[n]==INF) return -1;//如果第n个点路径为无穷大即不存在最低路径
map[n] out!!;