问题的提法是:给定一个没有负权值的有向图和其中一个点src作为源点(source),求从点src到其余个点的最短路径及路径长度。求解该问题的算法一般为Dijkstra算法。

假设图顶点个数为n,则针对其余n-1个点需要分别找出点src到这n-1个点的最短路径。Dijkstra算法的思想是贪心法,先找出最短的那条路径,其次找到次短的,再找到第三短的,依次类推,直到找完点src到达其余所有点的最短路径。下面举例说明算法和贪心过程。

如下图所示(该图源自《数据结构预(用面向对象方法与C++语言描述)(第2版)》殷人昆 主编清华大学出版社),求顶点0到其余个点的最短路径其路径长度。

说明一下,下面表述过程中有一个【直接到达】,例如 从点0直接到达点K意思是有一条边从0指向K。

从定点0出发能直接达到点1、3、4,不能直接到达的点,认为点0直接到达这些点的距离为无穷大,所以这些直接路径中,最短的路径为0到1,长度为10。可以认定,从0到点1的最短路径就是0→1,否则,假设存在另外一条从0到1的路径,经过其他点,例如点k,使得该路径0→k+k→3的路径长度<10,然而已知,从0直接到达除1之外的其他点k的距离 > 从0直接到达点1的距离,所以这条路径不可能是最短路径。这样,就找出了从0到1的最短路径。

然后,找出从0直接到达其他的点最短路径,最短为0→3,路径长度为30,那么能否像确定0→1一样确定0→3为从0到达3的最短路径呢?不能,因为有可能存在另外一条从0到3的路径,经过其他点,例如k,使得该路径0→k+k→3的路径长度<30,因为存在0→k的长度<30,所以有可能 路径(0→k)+(k→3)的路径长度<30,所以需要验证一下这种可能是否存在。

上面通过实例对Dijkstra算法的贪心过程进行了简单说明,下面给出算法过程。

Dijkstra算法过程:设图的邻接矩阵为G[n][n],顶点集合为V,G[i][j]表示从i到j的直线距离,求定点v到达其他定点的最短路径及路径长度。

Step1:令集合S={v},集合S用于存储已经找到从v到达该点的最短路径的顶点,dist[i]用于存储v到i的最短路径长度,初始化的时候令dist[i] = G[v][i](i取遍V-S中所有的点)。

Step2:寻找k使得dist[k]= min{dist[i]},令S=S+{k}(注意这里+表示取两个集合的并集)。

Step3:更新dist[i] = min { dist[i],dist[k]+G[k][i] } (i取遍V-S中所有的点)。

Step4:判断,若S==V,则停止算法,dist[i]即为v到i的最短路径长度;否则,转到Step2。

下面给出不严密证明:

v作为源点,算法开始的时候,没有找到任何点的最短路径,所以Step2寻找k使得dist[k]= min{dist[i]},此时k的最短路径已经找到,长度即为dist[k],因为从v直接到达到k的路径最短,若让v经过其他点再到达k,必然使得路径增长(这也是为什么要求图上的权值非负)。所以v到k路径找到,将k加入到集合S中。

进而Step3,看能否让从v到其他点i的路径经过k再到i,使得路径变短,若能则更改路径长度,否则不改变。这样到Step2继续找最短路径,寻找k使得dist[k]= min{dist[i]},对于找到的顶点k,最短路径也找到了的。如此循环下去,直到找到从v到所有点的最短路径。

Step4判断,是否从v到达所有点的最短路径都已经找到,即S==V。

问题1:在Step2和Step3中可能出现这样如图的问题,0为源点,首先选出最短路径的点一次为2和1,那么在仅剩下3,在运行Step2的时候,就需要考虑 dist[3] = min{dist[3],dist[1]+G[1][3]},dist[3]=无穷,达到3的最短路径必须通过1,这样最短路径为0->1->3,长度为13,比0->2->3的路径长度12要长,出现错误!错误在于,在这里进行Step2之前,在上一次循环中Step3的时候,已经对dist[3]进行更新,更新为dist[3]
= min{dist[3],dist[2]+G[2][3]},dist[3]=无穷大,dist[2]+G[2][3]=5+7=12,所以下一次循环的时候,dist[3]=12而非无穷,dist[3]=min{dist[3],dist[1]+G[1][3]}=12,

这样就不会出错了。关键是每一步都对没有找到的最短路径的点的路径长度进行了更新。

问题2:dist[i]记录了从v到达i的最短路径长度,如何记录从v到达每个点的具体的最短路径呢?

利用Dijkstra算法求出来的最短路径的特点:任取从v到达k的最短路径上的一个点i(非v非k),那么这条路径上从v到i的那部分路径就是从v到i的最短路径,因为Dijkstra是按照路径长度递增的顺序求最短路径的。这样,就可以在每次求出v到达一个点k的最短路径的时候,记录k之前的那个点,最后逆向即可找到具体路径。

下面通过Dijkstra算法求解前面的例子。

如图,求顶点0到其余个点的最短路径其路径长度。

初始化:S={0},dist[1]=10,dist[2]=INF,dist[3]=30,dist[4]=100,INF表示无穷大。另外增加一个数组path,记录找到最短路径的点之前的那个点,初始化的时候都初始化为path[i]=0(源点)。

Step2:min{dist[1]=10,dist[2]=INF,dist[3]=30,dist[4]=100}=dist[1],所以S={0,1}。

Step3:更新dist[]数组,dist[2]=min{dist[2],dist[1]+G[1][2]}=min{INF,10+50}=60,path[2]=1;

dist[3]=min{dist[3],dist[1]+G[1][3]}=min{30,10+INF}=30,path[3]不改变;

dist[4]=min{dist[4],dist[1]+G[1][4]}=min{100,10+INF}=100,path[4]不改变;

Step2:min{dist[2]=60,dist[3]=30,dist[4]=100}=dist[3],所以S={0,1,3}。

Step3:更新dist[]数组,dist[2]=min{dist[2],dist[3]+G[3][2]}=min{60,30+20}=50,path[2]=3;

dist[4]=min{dist[4],dist[3]+G[3][4]}=min{100,30+60}=90,path[4]=3;

Step2:min{dist[2]=50,dist[4]=90}=dist[2],所以S={0,1,3,2};

Step3:更新dist[]数组,dist[4]=min{dist[4],dist[2]+G[2][4]}=min{90,50+10}=60,path[4]=2;

Step2:只有dist[4]一个了,min{dist[4]}=dist[4],所以S={0,1,3,2,4}==V,停止算法。

对于1,path[1]=0,找到源点0,所以最短路径为0—>1,路径长度为dist[1]=10。

对于2,path[2]=3,path[3]=0,找到源点0,所以最短路径为0—>3—>2,路径长度为dist[2]=50。

对于3,path[3]=0,找到源点0,所以最短路径为0—>3,路径长度为dist[3]=30。

对于4,path[4]=2,path[2]=3,path[3]=0,所以最短路径为0—>3—>2—>4,路径长度为dist[4]=60。

用Dev-C++编写的C++代码:

#include<iostream>
using namespace std; int INF = 9999999; void Dijkstra(int *d[],int dist[],int path[],int n,int src)
{ //dist[i]记录从源点src到点i的路径长度,path[i]记录最短路径上点i前面的那个点
for(int i=0;i<n;i++) //dist[]和path[]的初始化
{
dist[i] = d[src][i];//dist[i]初始化为src到i之间的边的长度,没有从src指向i的边,那就是///无穷大
path[i] = src; //path[i]统一初始化为src,因为此时从src到i的路径就是从src到i的边
} bool *S = new bool[n]; //集合S,用于存储已经找到从src到达i的最短路径的点i
for(int i=0;i<n;i++) //初始化S
S[i] = false; //开始最短路径都没有找到,所以初始化false
S[src] = true; //点src已经找到(本来不用考虑src,但为后面代码简便,将src排除在 “未找到//最短路的点”之列) while(1) //看后面退出循环的条件
{
int temp = INF,k;
for(int i=0;i<n;i++) //对于所有的不在S中的i,找到 dist[i]的最小值
{
if(S[i]==false && dist[i]<temp)
{
k = i;
temp = dist[i];
}
} //最小值对应的点为k,最小值即为k的最短路径长度
S[k] = true; //将点k放入集合S
for(int i=0;i<n;i++) //更新每条路径的长度
{
if(S[i]==false && dist[k]+d[k][i]<dist[i]) //前面一个条件表明针对的是没有找到<span style="white-space:pre"> </span>//最短路径的点,后面条件表明路径经过点k则路径变短
{
dist[i] = dist[k] + d[k][i]; //更改路径长度
path[i] = k; //那么点i之前的那个点就是点k
}
} //下面检验是否退出循环---所有点是否都在S当中
bool ok = true;
for(int i=0;i<n;i++)
{
if(S[i]==false)
{
ok = false;
break;
}
}
if(ok==true)
break;
}
} int main()
{
const int n = 5;
int D[n][n] = {{0,10,INF,30,100},{INF,0,50,INF,INF},{INF,INF,0,INF,10},{INF,INF,20,0,60},{INF,INF,INF,INF,0}}; int **d = new int* [n];//不能直接传入D作为参数,需要进行一定的处理,将二维数组转化为一维数组,<span style="white-space:pre"> </span>//并且该数组类型为 int* 类型
for(int i=0;i<n;i++)
{
d[i] = new int[n];
for(int j=0;j<n;j++)
d[i][j] = D[i][j];
}
int path[n],dist[n];
int src = 0;
Dijkstra(d,dist,path,n,src);
for(int i=0;i<n;i++) //通过数组path[],寻找具体的最短路径,输出最短路径长度
{
if(i!=src)
{
int Road[n] = {i};
int k=1,temp = path[i];
while(1)
{
Road[k] = temp;
k ++;
if(temp == src) break;
else temp = path[temp];
}
cout << "从 " << src << " 到达 " << i << " 的最短路径长度为" << dist[i] << ",最短路劲为:";
for(int i=k-1;i>0;i--)
cout << Road[i] << "->";
cout << Road[0] << endl;
}
} return 0;
}

如有纰漏,请指正!

非负权值有向图上的单源最短路径算法之Dijkstra算法的更多相关文章

  1. 单源最短路径问题2 (Dijkstra算法)

    用邻接矩阵 /* 单源最短路径问题2 (Dijkstra算法) 样例: 5 7 0 1 3 0 3 7 1 2 4 1 3 2 2 3 5 2 4 6 3 4 4 输出: [0, 3, 7, 5, 9 ...

  2. 图论(四)------非负权有向图的单源最短路径问题,Dijkstra算法

    Dijkstra算法解决了有向图G=(V,E)上带权的单源最短路径问题,但要求所有边的权值非负. Dijkstra算法是贪婪算法的一个很好的例子.设置一顶点集合S,从源点s到集合中的顶点的最终最短路径 ...

  3. 单源最短路径问题之dijkstra算法

    欢迎探讨,如有错误敬请指正 如需转载,请注明出处 http://www.cnblogs.com/nullzx/ 1. 算法的原理 以源点开始,以源点相连的顶点作为向外延伸的顶点,在所有这些向外延伸的顶 ...

  4. 单源最短路径—Bellman-Ford和Dijkstra算法

    Bellman-Ford算法:通过对边进行松弛操作来渐近地降低从源结点s到每个结点v的最短路径的估计值v.d,直到该估计值与实际的最短路径权重相同时为止.该算法主要是基于下面的定理: 设G=(V,E) ...

  5. 单源最短路径问题1 (Bellman-Ford算法)

    /*单源最短路径问题1 (Bellman-Ford算法)样例: 5 7 0 1 3 0 3 7 1 2 4 1 3 2 2 3 5 2 4 6 3 4 4 输出: [0, 3, 7, 5, 9] */ ...

  6. 图->最短路径->单源最短路径(迪杰斯特拉算法Dijkstra)

    文字描述 引言:如下图一个交通系统,从A城到B城,有些旅客可能关心途中中转次数最少的路线,有些旅客更关心的是节省交通费用,而对于司机,里程和速度则是更感兴趣的信息.上面这些问题,都可以转化为求图中,两 ...

  7. 单源最短路径-迪杰斯特拉算法(Dijkstra's algorithm)

    Dijkstra's algorithm 迪杰斯特拉算法是目前已知的解决单源最短路径问题的最快算法. 单源(single source)最短路径,就是从一个源点出发,考察它到任意顶点所经过的边的权重之 ...

  8. 单源最短路径 Bellman_ford 和 dijkstra

    首先两个算法都是常用于 求单源最短路径 关键部分就在于松弛操作 实际上就是dp的感觉 if (dist[e.to] > dist[v] + e.cost) { dist[e.to] = dist ...

  9. 单源最短路:Dijkstra算法 及 关于负权的讨论

    描述: 对于图(有向无向都适用),求某一点到其他任一点的最短路径(不能有负权边). 操作: 1. 初始化: 一个节点大小的数组dist[n] 源点的距离初始化为0,与源点直接相连的初始化为其权重,其他 ...

随机推荐

  1. htm,html,xhtml,xml,xsl,dhtml,shtm和shtml的区分

    介绍一下htm,html,xhtml,xml,shtml的区分,以下内容来自百度后的知识整理. HTML和htm: HTML(Hypertext Markup Language)超文本传输语言,是ww ...

  2. PokeCats开发者日志(七)

      现在是PokeCats游戏开发的第十二天的晚上,很不幸提交到的三个平台(360开放平台,腾讯开放平台,华为应用市场)都没通过,著作权申请也被打回来了.   心中一万只草泥马在奔腾.   得了,看来 ...

  3. Object empty value key filter

    Object empty value key filter 过滤空值 Utils emptykeysFilter() "use strict"; /** * * @author x ...

  4. BZOJ2001 [Hnoi2010]City 城市建设 【CDQ分治 + kruskal】

    题目链接 BZOJ2001 题解 CDQ分治神题... 难想难写.. 比较朴素的思想是对于每个询问都求一遍\(BST\),这样做显然会爆 考虑一下时间都浪费在了什么地方 我们每次求\(BST\)实际上 ...

  5. 【CF edu 30 A. Chores】

    time limit per test 2 seconds memory limit per test 256 megabytes input standard input output standa ...

  6. 【COGS 461】[网络流24题] 餐巾 最小费用最大流

    既然是最小费用最大流我们就用最大流来限制其一定能把每天跑满,那么把每个表示天的点向T连流量为其所需餐巾,费用为0的边,然后又与每天的餐巾对于买是无限制的因此从S向每个表示天的点连流量为INF,费用为一 ...

  7. [8.16模拟赛] 玩具 (dp/字符串)

    题目描述 儿时的玩具总是使我们留恋,当小皮还是个孩子的时候,对玩具更是情有独钟.小皮是一个兴趣爱好相当广泛且不专一的人,这这让老皮非常地烦恼.也就是说,小皮在不同时刻所想玩的玩具总是会不同,而有心的老 ...

  8. How to setup Active Directory (AD) In Windows Server 2016

    Windows Server 2016 is the newest server operating system released by Microsoft in October 12th, 201 ...

  9. eclipse+jetty+web项目调试---不显示源码

    本人eclipse版本:JUNO 1.问题现象:显示源码时,不显示箭头(指示到哪行) 解决办法: debug configurations  --->Goals设置参数  clean -X je ...

  10. HDU1878 欧拉回路---(并查集+图论性质)

    http://acm.hdu.edu.cn/showproblem.php?pid=1878 欧拉回路 Time Limit: 2000/1000 MS (Java/Others)    Memory ...