最短路进阶
最短路进阶
回顾
- 按照最短路径的长度递增的次序,依次求得——源点到其余各点的最短路径!
- 先求——最短的那条最短路径(特点?)
- 再求——第二短的最短路径(特点?)
- ……
- 依次类推(特点?)
- ……
- 最终求出所有的最短路径





代码

迪杰斯特拉的堆优化
基本思想:
采用邻接表存储图,并利用优先队列维护每个节点的Dist值,避免了每次从n个节点找一个最优解,可以O(1)时间得到本趟的最短路,代价是松弛中的更新操作,每次需要O(LogE),因为总的松弛操作数和E成正比,所以——
优化后的时间复杂度:ElogE

例题1
1967年,美国著名的社会学家斯坦利·米尔格兰姆提出了一个名为“小世界现象”的著名假说,任何2个素不相识的人中间最多只隔着6个人,即只用6个人就可以将他们联系在一起,因此他的理论也被称为“六度分离”理论。
小胖子对N个人展开了调查,得到了他们之间的相识关系,现在他请你帮忙验证“六度分离”是否成立。
每组输入数据的第一行包含两个整数N,M,分别代表人数(编号1~N),以及关系数。接下来有M行,每行两个整数A,B表示A和B认识。
如果符合“六度分离”理论,输出Yes,否则输出No。
Sample Input
8 7
1 2
2 3
3 4
4 5
5 6
6
7
7 8
Sample Output
Yes
floyd(\(n^3\))
Floyd算法(插点法,经典DP)
- 直接上算法核心代码:
for(k=1;k<=n;k++) //插入点k在最外层循环很关键! |
算法思想:从i到j的只经过前k个点的最短路径
算法特点:简单、粗暴、易于实现、可以解决负权
Bellman-Ford
- 直接上算法核心代码(数据存储结构?):
for(k=1;k<=n-1;k++) //n-1个阶段,每个阶段的效果?
for(i=1;i<=m;i++)//每个阶段,对所有边尝试松弛
if(dis[v[i]] > dis[u[i]] + w[i])
dis[v[i]] = dis[u[i]] + w[i]
算法思想:对所有的边进行n-1次松弛操作!
算法复杂度:\(O(nm)\)(比Dijkstra还要慢?) (小优化:不一定需要n-1轮松弛,想想冒泡排序)
算法特点:简洁、可以解决负权负环的情况
增加点数量,点的数量
检测负环:需要Bellman-Ford
Bellman-Ford算法检测负环
- 直接上算法核心代码:
for(k=1;k<=n-1;k++) //为什么只需要循环n-1次? |
有负环的情况
SPFA
Bellman-Ford算法的队列优化(SPFA)
基本思想:每次仅对最短路程发生变化了的点的相邻边执行松弛操作。
问题:如何知道当前哪些点的最短路程发生了变化呢?
方案:采用 “队列” 维护
具体操作:先把起点加进队列中,然后松弛和起点相连的所有边,如果松弛成功且该点不在队列中,那么就把这个点入队。然后依次取出队列中的每一个点进行松弛,直到队列为空。
SPFA参考代码:
void spfa(int u) { |
小结
几种最短路算法的比较分析
Dijkstra,Floyd,Bellman-Ford(SPFA)
时间复杂度? 负权边的情况? 判断是否有负权回路?