dijkstra
这几天才开始真正学习图论,
第一天考试就有恶心的图论题。。。。。我不得不开始学习dijkstra。。。。(spfa先放一放吧)
Dijkstra算法是一种求单源最短路的算法,即从一个点开始到所有其他点的最短路。 其基本原理是:每次新扩展一个距离最短的点,更新与其相邻的点的距离。当所有边权都为正时,由于不会存在一个距离更短的没扩展过的点,所以这个点的距离永 远不会再被改变,因而保证了算法的正确性。不过根据这个原理,用Dijkstra求最短路的图不能有负权边,因为扩展到负权边的时候会产生更短的距离,有 可能就破坏了已经更新的点距离不会改变的性质。
如果用本算法求一个图中全部的最短路,则要以每个点为源调用一次Dijkstra算法。
算法流程
在以下说明中,s为源,w[u,v]为点u和v之间的边的长度,结果保存在dist[]
- 初始化:源的距离dist[s]设为0,其他的点距离设为无穷大,同时把所有的点的状态设为没有扩展过。
- 循环n-1次:
- 在没有扩展过的点中取一距离最小的点u,并将其状态设为已扩展。
- 对于每个与u相邻的点v,执行Relax(u,v),也就是说,如果dist[u]+w[u,v]<dist[v],那么把dist[v]更新成更短的距离dist[u]+w[u,v]。此时到点v的最短路径上,前一个节点即为u。
- 结束。此时对于任意的u,dist[u]就是s到u的距离。
算法实现
- 直接实现
最简单的实现方法就是,在每次循环中,再用一个循环找距离最短的点,然后用任意的方法更新与其相邻的边,时间复杂度显然为O(n2)
对于空间复杂度:如果只要求出距离,只要n的附加空间保存距离就可以了(距离小于当前距离的是已访问的节点,对于距离相等的情况可以比较编号或是特殊处理一下)。如果要求出路径则需要另外V的空间保存前一个节点,总共需要2n的空间。
- 二叉堆实现
使用二叉堆(Binary Heap)来保存没有扩展过的点的距离并维护其最小值,并在访问每条边的时候更新,可以把时间复杂度变成O(n+mlogn)。
当边数远小于点数的平方时,这种算法相对来说有很好的效果。但是当m=O(n2)时(有时候表现为不限制边的条数),用二叉堆的优化反倒会更慢。因为此时的复杂度是O(n+n2logn),大于不用堆的实现的O(n2)的复杂度。
另外此时要用邻接表保存边,使得扩展边的总复杂度为O(m),否则复杂度不会减小。
空间复杂度:这种算法需要一个二叉堆,及其反向指针,另外还要保存距离,所以所用空间为3n。如果保存路径则为4n。
具体思路:先将所有的点插入堆,并将值赋为极大值(maxint/maxlongint),将原点赋值为0,通过松弛技术(relax)进行更新以及设定为扩展。
code:
网上的代码巨恶心。。。。
以下是我刚学的朴素dijkstra。
1 program dijkstra;
2 var
3 f,min,minn,i,j,n,m,k,l:longint;
4 a:array[1..1000,1..1000]of longint;
5 b:array[1..1000]of longint;
6 begin
7 assign(input,'dj.in');reset(input);
8 readln(n);
9 for i:=1 to n do
10 for j:=1 to n do
11 begin
12 read(a[i,j]);
13 if a[i,j]=0 then a[i,j]:=maxlongint-10000;
14 end;
15 fillchar(b,sizeof(b),0);
16 b[1]:=1;
17 minn:=1;
18 for k:=2 to n do
19 begin
20 min:=maxlongint;
21 for i:=2 to n do
22 if (a[1,i]<min)and(b[i]=0)then
23 begin
24 min:=a[1,i];
25 minn:=i;
26 end;
27 b[minn]:=i;
28 for j:=1 to n do
29 if (j<>minn)and(a[1,minn]+a[minn,j]<a[1,j])and(b[j]=0) then
30 a[1,j]:=a[1,minn]+a[minn,j];
31 end;
32 for i:=1 to n do if a[1,i]=maxlongint-10000 then a[1,i]:=-1;
33 for i:=1 to n do write(a[1,i],' ');
34 close(input);
35 end.
参考:http://www.nocow.cn/index.php/Dijkstra%E7%AE%97%E6%B3%95