Dikstra算法解决的是有向图上单源最短路径问题(无向图可以看成有相反的两条有向边),且要求边的权重都是非负值(如果有负权,已经确定的最短路径到v点有可能会被右面发现的点有条负权值的边到v点而导致v点的最短路径不成立)。
算法导论用了很多引理,性质来证明Dijstra算法的正确性,这里不说了,也表达不明白,只说我理解的过程。
有一个图G( V,E) ,选定一个源点s,维护一个集合Q=V-s, Q中点有一个d值表示此时从s到该点的已知距离,s.d=0 ;初始化都为正无穷,表明不可达。然后对s点所连接的点(设为点集M)进行松弛操作,就是设点m属于M,
m.d > s.d+ w(s,m) 则更新 m.d=s.d+w(s,m) 将其慢慢收敛。
Q中的点能够松弛的松弛完之后,取出此时d值最小的点的,把它看成上面的s点,对其所连接点的进行松弛操作,然后再取点,重复直到Q集合为空。s点到所有点的最短路径就找到了。
再来说说实现的问题:
1.Q集合用数组来实现,我在实现的时候Q数组存在的是对应点的d值,如果这个点已经被取出则变为1;这样所有的开销都在去最小值上了,因为每次都要扫描整个数组。取点要V次,一次扫描O(V) ,所以总运行时间O(V^2);
2.Q集合用优先队列实现,但是因为要有decrease-key操作,所以貌似不能用标准库的优先队列。所以只能自己实现一个最小堆的优先队列。因为松弛的时候是找到点,然后又要去更改Q中的对应点的d值,然后在进行调整,所以要有互相指向的句柄。
取点V次,每次Extract-min花费O(lgV)
decrease-key E次 ,每次花费 O(lgV)
总花费 O( (E+V)lgV )
数组方法的时候,代码不贴出来来了,带句柄的数组建最小优先队列的方法我贴出来,其实句柄就是一个指向元素在数组堆中的位置,因为要建堆,维护堆,取出最小值等操作会因为位置变化,所以要在几个操作里面更新句柄。
Dijkstra.cpp
#include<iostream>
#include<fstream>
#include<queue>
#include"MiniPriorityQueu