图论 最短路径算法 2.Dijkstra算法

Posted uncklesam7

tags:

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

Dijkstra 算法解决的是带权重的有向图上单源最短路径问题,该算法要求所有边的权重都为非负值。该算法的时间复杂度是O(N2),相比于处理无负权的图时,比Bellmad-Ford算法效率更高。

算法描述:

首先引用《算法导论》中的一段比较官方的话,如果可以看懂,那下一部分就可以跳过了:

“Dijkstra算法在运行过程中维持的关键信息是一组结点集合S。从源结点s到该集合中每个结点之间的最短路径已经被找到。算法重复从结点集 V - S 中算则最短路径估计的最小的结点 u ,将 u 加入到集合S,然后对所有从 u 出发的边进行松弛。” 所谓松弛操作,简单的说就是更新两点间的最短距离。

不是很好理解对吧,那么下面的描述是更容易理解的一种描述:

设起始点为s,dis[v]表示s点到v点的最短路径,pre[v]是v的前驱结点,用来输出路径。

1、初始化:dis[v]=∞(v≠s) dis[s]=0,pre[s]=0;

2、for(i=1;i<=n;i++)

(1)在没有被访问过的点中,即上述的V - S集合,找到一个点 u 使得dis[u]是最小的。

(2)标记 u 为已确定的最短路径。

(3)for(每个与 u 相连且没有确定过最短距离的点 v)     

if(dis[u]+m[u][v]<dis[v]){

  dis[v]=dis[u]+m[u][v];

  pre[v]=u;

}

3、结束:结果dis[v]就是s到v的最短距离。

算法理解:

其中自以为有几点理解需要说明:

1、为什么用到中间点?

2、取s到中间点的距离时采用什么策略?

第一个问题,从起点到另一个点的最短路径至少会经历一个中间点,所以我们要求出经过这个中间的到另一个点的路径,就要先求出起点到中间点的最短路径。

第二个问题,其实这里采用的是一中贪心的策略。当然这个策略可以被严格证明是正确的,但是我也一知半解,只知道是可以被证明的,在这里也就不浪费时间了。(详细可以参考《算法导论》)

最后,解释一下为什么有负权边的时候不可以:

连接矩阵如下(图可以自己在旁边画一下):

  1 2 3

1 \ 2 1

2 2 \ -4

3 1 -4 \

那么第一次标记的点就为3并且把dis[3]记为1,但实际上dis[3]应该时-2,因此就会出现错误。

最后附上一段不那么标准的代码:

 1 #include<stdio.h>
 2 #include<stdlib.h>
 3 int m[100][100],e,dist[100],n,b[100],pre[100],dist[100];
 4 void dij(int s){
 5      b[s]=1;
 6      int i,j;
 7      for(i=1;i<=n;i++)
 8       dist[i]=m[s][i];
 9      dist[s]=0;
10      pre[s]=0;
11      
12      for(i=1;i<=n;i++){
13       int min=1000000,k=0;
14       for(j=1;j<=n;j++)
15        if(b[j]!=1 && dist[j]<min)
16         {min=dist[j];k=j;}
17        b[k]=1;
18        for(j=1;j<=n;j++)
19         if(min+m[k][j]<dist[j]&&b[j]!=1)
20          {
21           dist[j]=min+m[k][j];
22           pre[j]=i;
23           }                                
24       }
25       for(i=1;i<=n;i++)
26        if(i!=s)
27         printf("%d ",dist[i]);
28      }
29 int main(){
30     int i,j;
31     scanf("%d%d",&n,&e);
32     memset(b,0,sizeof(b));
33     memset(m,10000,sizeof(m));
34     for(i=1;i<=e;i++){
35      int x,y;
36      scanf("%d%d",&x,&y);
37      scanf("%d",&m[x][y]);
38      }
39     int w;
40     scanf("%d",&w);
41     dij(w);
42     system("pause");
43     return 0;
44     }

 

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

图论:图的四种最短路径算法

图论:图的四种最短路径算法

最短路径问题之Dijkstra算法(C语言)

图论之最短路径floyd算法

用小根堆实现dijkstra,求图的单源最短路径

图论 最短路径算法 1.Floyed-Warshall算法