算法流程
在以下说明中,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)进行更新以及设定为扩展。
菲波那契堆实现
- 用类似的方法,使用Fibonacci Heap可以将复杂度降到O(m+nlogn),但实现比较麻烦。因此不宜在时间较短的信息学竞赛中使用。详见Fibonacci堆。