Dijkstra是解决无负权边无负环的带权图中单源最短路径问题的经典算法。它的大致过程是:以起点为中心,每次取最轻(最短)的一个顶点,更新其相邻结点的最小距离——其实就是运用了贪心思想(简单地说就是每次选取局部最优解),向外层层拓展,直至找遍起点到其他点的最短距离。下面给出算法的流程:
设源点为s,且从s到其他所有顶点的边权均已确定,设集合S初始时为空集;设dist数组,dist[i]表示s到i的最短距离,且dist数组初始值设为s到各点的边权;每次选取一个点u∈(V-S)(即V集合中满足不在S集合中的元素),使得dist[u]为(V-S)中所有点最小dist值;将u加入S,然后枚举u相邻的点,并更新其相邻的点的dist值(对u的所有出边进行松弛——此操作详见代码分析)。
显然,每个顶点只能入集合S一次,所以上述操作最多重复|V|次,又因为对于S中的每个顶点要枚举它相邻的所有结点,即最多松弛|V|次,设n为点数,故Dijkstra算法的时间复杂度为O(n^2);用邻接矩阵存储图,空间复杂度亦为O(n^2)。
算法适用范围:
1、单源最短路径问题;
2、Dijkstra算法要求图G无负权边,即对于任意(u,v)∈E,要求有ω(u,v)>=0;否则,对贪心策略的顶点选择会有影响;
证明:最短路径问题运用了贪心思想,即保证当前dist数组存储的是局部最优解。亦即是说,dist数组满足min(dist[i])(i=1,2,3,..,|V|),是当前最优值。在图中边权非负的情况下,图满足上述性质,而存在负权边时则不满足,因为有可能从一个较大的dist值更新能得到一个比当前min(dist[i])(i=1,2,3,..,|V|)更优的值。
3、Dijkstra算法要求图G无负环,单源最短路问题对于有负环的图是无解的,但Dijkstra算法不能检测出负环。
贴上部分代码:
(C++ Code)
1 void Dijkstra(int s,int n)
//s为源点,n为点数
2 {
3 memset(dist,100,sizeof(dist));
4 memset(t,false,sizeof(t));
//t数组用于判断点是否在集合S中,若在,则t[i]=true;
5 for (int i=1;i<=n;i++)
6 dist[i]=map[now][I];
//初始化dist数组
7 dist[s]=0;
8 t[s]=true;
9 for (int i=1;i<n;i++)
10 {
11 int min=100;
12 for (int j=1;j<=n;j++)
13 if (!t[j]&&dist[j]<min)
14 {
15 min=dist[j];
16 k=j;
17 }
18 t[k]=true;
//将最近(轻)的点加入集合S中
19 for (int j=1;j<=n;j++)
20 if(!t[j]&&dist[k]+map[k,j]<dist[j])
21 dist[j]=dist[k]+map[k,j];
//以上为松弛过程,松弛边的操作其实就是更新当前k相邻点的dist值
22 }
23 }