单源最短路径Dijkstra算法

Posted 算法入门

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了单源最短路径Dijkstra算法相关的知识,希望对你有一定的参考价值。

一、前言 
Dijkstra算法又称单源最短路径算法,顾名思义此算法限定了只有一个源点。这种算法就不适用于求任意两点的最短路径。同时,Dijkstra算法只能处理不存在负权回路的图,这点会在下面细讲。 
二、概念 
这里我们讲解最短路径,需要掌握几个基本的概念: 
对于有向图G=(V,E),权值函数W: E→R(即每条边的权值都为一个实数) 
1、路径 
p:v1->v2->v3->...vk 
表示从v1到vk的一条路径 
2、最短路径: 
从u到v的一条路径,使w(p)最小,w(p)。 
3、最短路径权值: 
 
注意,最短路径可能不存在 
(1)当图中存在负权回路,例如: 
单源最短路径Dijkstra算法 
可以看出,存在v1到v6的负权回路,它的权值为-3,如果我们想找从u到v的最短路径,那么无限循环地走这个负权回路可以使最短路径越来越小,最后达到负无穷,那么就说明找不到从u到v的最短路径。 
(2)不存在从u到v的路径,这个是肯定不会存在最短路径的。 
三、最优子结构 
求解源点到某一顶点的最短路径,其实不比求解源点到所有顶点的路径简单。这个时候我们要引入全局的概念,能不能找出所有的顶点的最短路径,然后再去查看到目标点的最短路径呢?很多人就会想到动态规划这一思想,说道动态规划,自然我们首先要考虑的问题是最优子结构。 
最短路径满足最优子结构性质:最短路径的子路径是最短路径。 
单源最短路径Dijkstra算法
前提:u到v是最短路径。 
证明:(剪贴法) 
假设:x到y不是最短路径,那么存在一条更短的路径从x到y(假设为下面的弯箭头),这样,删去原路径中从x到y的路径,用新找到的路径替代(弯箭头),那么就得到了一条比u到v的权值更短的路径,这与前提u到v是最短路径相矛盾,因而x到y是最短路径,即最短路径满足最优子结构性质。 
引入三角不等式的概念:(从u到v的最短路径权值,小于等于从u到x的最短路径权值加上从x到v的最短路径权值) 单源最短路径Dijkstra算法 
这个性质根据最优子结构性质而来,非常重要。 
四、Dijkstra算法思想 
设G=(V,E)是一个带权有向图,把图中顶点集合V分成两组,第一组为已求出最短路径的顶点集合(用S表示,初始时S中只有一个源点,以后每求得一条最短路径 , 就将 加入到集合S中,直到全部顶点都加入到S中,算法就结束了),第二组为其余未确定最短路径的顶点集合(用U表示),按最短路径长度的递增次序依次把第二组的顶点加入S中。在加入的过程中,总保持从源点v到S中各顶点的最短路径长度不大于从源点v到U中任何顶点的最短路径长度。此外,每个顶点对应一个距离,S中的顶点的距离就是从v到此顶点的最短路径长度,U中的顶点的距离,是从v到此顶点只包括S中的顶点为中间顶点的当前最短路径长度。 
五、简单例子说明 
初始情况: 
单源最短路径Dijkstra算法
第一次松弛,选取A顶点: 
单源最短路径Dijkstra算法
第二次松弛,C的估算距离最小,选取C顶点: 
单源最短路径Dijkstra算法
第三次松弛,E的估算距离最小,选取E: 
单源最短路径Dijkstra算法
第四次松弛,B的估算距离最小,选取B: 
单源最短路径Dijkstra算法
第五次松弛:(最后一个点,完成) 

经过所有的松弛操作之后,我们就得到了所有顶点的最短路径(表格中红字部分)。 
举个例子 
HDU2544 
在每年的校赛里,所有进入决赛的同学都会获得一件很漂亮的t-shirt。但是每当我们的工作人员把上百件的衣服从商店运回到赛场的时候,却是非常累的!所以现在他们想要寻找最短的从商店到赛场的路线,你可以帮助他们吗?

输入包括多组数据。每组数据第一行是两个整数N、M(N<=100,M<=10000),N表示成都的大街上有几个路口,标号为1的路口是商店所在地,标号为N的路口是赛场所在地,M则表示在成都有几条路。N=M=0表示输入结束。接下来M行,每行包括3个整数A,B,C(1<=A,B<=N,1<=C<=1000),表示在路口A与路口B之间有一条路,我们的工作人员需要C分钟的时间走过这条路。 输入保证至少存在1条商店到赛场的路线。

对于每组输入,输出一行,表示工作人员从商店走到赛场的最短时间

input:

 
   
   
 
  1. 2 1

  2. 1 2 3

  3. 3 3

  4. 1 2 5

  5. 2 3 5

  6. 3 1 2

  7. 0 0

output:

 
   
   
 
  1. 3

  2. 2

思路:直接Dijkstra 
Dijkstra算法和prime算法的代码差不多,就多了lowcost[k]; 
代码数据结构用邻接矩阵实现,当然还可以用邻接表实现,,优先队列优化 
次版本时间复杂度O(N^2)

 
   
   
 
  1. int edge[110][110];

  2. int lowcost[110];

  3. int vis[110];

  4. int n,m;

  5. void Dj(int cur)

  6. {

  7.    cle(vis);

  8.    vis[cur]=1;

  9.    for(int i=1;i<=n;i++)

  10.        lowcost[i]=edge[cur][i];

  11.    int k,Min;

  12.    while(1)

  13.    {

  14.        Min=INF;

  15.        for(int j=1;j<=n;j++)

  16.            if(!vis[j]&&Min>lowcost[j])

  17.        {

  18.            Min=lowcost[j];k=j;

  19.        }

  20.        if(Min==INF)break;

  21.        vis[k]=1;

  22.        for(int j=1;j<=n;j++)

  23.        {

  24.            if(!vis[j]&&lowcost[j]>lowcost[k]+edge[k][j])

  25.                lowcost[j]=lowcost[k]+edge[k][j];

  26.        }

  27.    }

  28.    cout<<lowcost[n]<<endl;

  29. }

  30. int main()

  31. {

  32.    //freopen("in.txt","r",stdin);

  33.    //freopen("out.txt","w",stdout);

  34.    int a,b,c;

  35.    while(cin>>n>>m)

  36.    {

  37.        if(n==0&&m==0)break;

  38.        for(int i=1;i<=n;i++)

  39.           for(int j=1;j<=n;j++)

  40.              { if(i==j){edge[i][j]=0;edge[j][i]=0;}

  41.                  else {edge[i][j]=INF;edge[j][i]=INF;}

  42.              }

  43.        for(int i=1;i<=m;i++)

  44.        {

  45.            cin>>a>>b>>c;

  46.            if(c<INF)

  47.            {   edge[a][b]=c;

  48.                edge[b][a]=c;

  49.            }

  50.        }

  51.        Dj(1);

  52.    }

  53.    return 0;

  54. }

  55. //年轻的代码  


 
欢迎关注:)


以上是关于单源最短路径Dijkstra算法的主要内容,如果未能解决你的问题,请参考以下文章

单源最短路径Dijkstra算法

Dijkstra求解单源最短路径

Bellman-ford 单源最短路径算法

Dijkstra算法详细(单源最短路径算法)

单源最短路径Dijkstra和优先级算法

Dijkstra算法求单源最短路径