最短路算法
Dijkstra算法
- 首先初始化一个距离数组为无穷
- 迭代n次:寻找不在集合中并且
dist
最小的点t
,并且用这个点更新其他所有点的距离,并把t
加入到集合中去
因此时间复杂度为O(n2)
堆优化Dijkstra算法
- 首先初始化一个距离数组为无穷,并把定点入堆。
堆中元素为pair<dist, #node>
,因此堆一直都是按照dist
排序的,从而把寻找最小dist
的操作降低到O(logn) - 每次从堆头
pop
一个元素t
,并且用这个点更新其他所有点的距离(考察他的所有邻接节点,如果可以更新则更新,如果还没有入堆则入堆),并把t
加入到集合中去
因此时间复杂度为O(mlogn)
-
可以看出,“选取
dist
最小的不在集合中节点”这一步属于贪心操作,导致Dijkstra算法不能应用于含负权边的情况 -
采用邻接表后内层循环只更新了联通的节点,因此复杂度是O(m);邻接矩阵实现采用
INF
或者-1
来代表不连通,但即使采用if
特判,内层循环实际上是Θ(n)的。(可能堆优化版本的常数小一点?)
Bellman-Ford算法
- 迭代n次:对每条边,根据是否走这条边更新终点的
dist
因此时间复杂度为O(nm)
SPFA算法
- 首先把起点加入队列
- 队列头节点出队。对于这一节点的所有出边,如果可以走这条边更新终点的最点距离,则终点入队并且更新距离
因此时间复杂度为O(m),最坏为O(nm)
- 注意SPFA中的
st
代表的是节点是否在堆中
Floyd算法
- 三重for,外层为点A,内层为点B,C,更新dist(B,C)=min(dist(B,C),dist(B,A)+dist(A,C))
因此时间复杂度为O(n3)
最短路算法对比
Kruskal | 堆Kruskal | Bell-Ford | SPFA | Floyd | |
---|---|---|---|---|---|
最短路类型 | 单源 无负权 | 单源 无负权 | 单源 含负权 | 单源 含负权 | 多源、汇 |
时间复杂度 | O(n2) | O(mlogn) | O(nm) | O(m),最坏O(nm) | O(n3) |
选用数据结构 | 邻接矩阵 | 邻接表 | 边集 | 邻接表/邻接矩阵 | 距离矩阵 |
选用数据结构原因 | 适用于稠密图 | 适用于稀疏图 | 方便遍历所有边 | 都便于找到一个点的所有出边,可以根据图稀疏性选择 | 可同时表示连通性和距离。初始化为对角线0,其他INF |
适用问题类型 | 全为正边的最短路 | 全为正边的最短路 | 限制路径长度的最短路 | 含负权最短路;求负环 | 多源、汇最短路 |