Dijkstra算法是一种计算单源最短无负边路径问题的常用算法之一,时间复杂度为O(n2)
算法描述如下:dis[v]表示s到v的距离,pre[v]为v的前驱结点,用以输出路径,vis[v]表示该点最短路径是否已经确认
初始化:dis[v]=INT dis[s]=0 pre[s]=0
执行n次
在没有确定的点中找到一个路径最短的,并修改为已经确认
通过这个点修改其他所有没有确定的点
直到所有点已经确认为最短路径,退出循环
现在证明算法的正确性:
首先,我们说明两条性质:
1.确定任何一个点为最短路径时,前驱p一定已经是最短路径(假设源点的前驱是它本身)。
2.任意时刻在还没有确定最短路径的点的集合中路径最短的点就是可以被确定的点。
稍微证明一下这两条性质(反证法):
证明1:假如前驱p还不是最短路径,那么显然当p是最短路径时到当前节点的路径更短,即不是最短路径的节点所确定的节点一定不是最短路径(好像很显然)
证明2:为了方便描述,我们定义已经确定的点为白点,没有确定的点为蓝点。若是蓝点中路径最短x还不能确定为白点,那么x的最短路径上必定存在一个蓝点y。即dis[x]>dis[y]+edge[y][x]。而dis[y]处于两种状态:
(1)dis[y]已经是y点的最短路径,那么显然与dis[x]为蓝点中最短的相矛盾。
(2)dis[y]还不是y点的最短路径,那么它的前驱显然是另外一个蓝点,即dis[y]>=dis[x](这里省略了一些步骤,不过自己稍微想一下,我觉得挺显然的),也矛盾。
综上,性质2得证。
回过头再看我们的算法:
在刚开始的时候,首先确定的最短路(就是直接和源点相连的点)进入白点集合,满足性质1,2。
此后每个过程都满足以上两个性质,所以算法正确。
算法实现:
1 memset(vis,0,sizeof(vis));
2 vis[s]=1;
3 dis[s]=0;
4 for(int i=1;i<n;i++)
5 {
6 int m=INT_MAX;
7 int k=0;
8 for(int j=1;j<=n;j++)
9 {
10 if(!vis[j] && dis[j]<m)
11 {
12 m=dis[j];
13 k=j;
14 }
15 }
16 if(k==0)
17 break;
18 vis[k]=1;
19 for(int j=1;j<=n;j++)
20 {
21 if(dis[k]+e[k][j]<dis[j])
22 dis[j]=dis[k]+e[k];
23 }
24 }