题目描述
思路解析动画文字版
最朴素的做法:每一轮在所有还没敲定的点里线性扫一遍,挑出当前 dist 最小的那个去确定。点一多,每轮都把全部距离重新比一遍,大量重复比较。
转折:与其每轮线性扫找最小,不如把 (距离, 点) 全丢进最小堆,弹出即取到当前最近的点。它一旦弹出,最短距离就敲定(非负权保证后面不会更短),再用它去松弛邻居:邻居新距离更短才更新并入堆。能用这招的前提是边权非负。
建图 · 源点 2=0:建带权邻接表。源点 2 的 dist=0,放进最小堆 (0,2);其余点 dist 视为 ∞。边上的数字是权(用时)。
弹出 2 · 敲定源点:弹出堆顶 (0,2),它的距离 0 就是 2 的最终最短距离,点 2 敲定(done)。接下来要逐条松弛它指出去的边 2→1 和 2→3。
松弛 1 和 3:松弛 2 的两条出边:dist[1]=0+1=1、dist[3]=0+4=4,都比 ∞ 小,更新并各自入堆 (1,1)、(4,3)。注意 3 暂时是 4,后面会被改短。
弹出 1 · 松弛 3「更短」更新:弹出 (1,1),点 1 敲定。松弛边 1→3:新距离 1+1=2,比旧的 4 更短!更新 dist[3]=2 并把 (2,3) 入堆。堆里此刻同时有旧的 (4,3) 和新的 (2,3)——同一个点可能多次入堆。
弹出 3 · 松弛 4:弹出更小的 (2,3),它的距离 2 与 dist[3] 一致,是有效记录,点 3 敲定。松弛边 3→4:dist[4]=2+1=3,入堆 (3,4)。
弹出 4 · 无出边:弹出 (3,4),点 4 敲定。它没有出边,不松弛任何点。堆里只剩一个 (4,3)——这是点 3 早先的旧记录。
弹出过期记录 (4,3) · 跳过:负例:弹出 (4,3),但它带的距离 4 大于现在的 dist[3]=2,是被更短路径淘汰掉的过期记录——直接 continue 跳过,不重复松弛。这就是代码里那行 if d 大于 dist[u]: continue 的作用。
全部确定 · 答案=最大 dist:堆空了,四个点全部敲定:dist[2]=0、dist[1]=1、dist[3]=2、dist[4]=3。信号传遍全网的时间 = 所有最短距离里的最大值 = 3。若有某个点仍是 ∞(没进过 dist),说明传不到,返回 −1。
和拓扑排序的 BFS 区别只有一点:拓扑用普通队列(谁先来谁先出),带权最短路必须用最小堆按距离取点,否则会先确定一个其实还能更短的点,结果就错了。
边界三连:三种边界先想清。
面试官常追问:四个高频追问,Dijkstra 必背。
参考代码
import heapqclass Solution: def networkDelayTime(self, times, n, k): graph = [[] for _ in range(n + 1)] for u, v, w in times: graph[u].append((v, w)) dist = [10**18] * (n + 1) dist[k] = 0 heap = [(0, k)] while heap: d, node = heapq.heappop(heap) if d > dist[node]: # 过期记录:已被更短路更新过,跳过 continue for nxt, w in graph[node]: if d + w < dist[nxt]: # 松弛 dist[nxt] = d + w heapq.heappush(heap, (d + w, nxt)) ans = max(dist[1:]) return -1 if ans == 10**18 else ans复杂度
- 时间复杂度:O(E·logV),每条边最多触发一次入堆,堆操作 logV;E 条边共 E·logV
- 空间复杂度:O(V+E),邻接表 O(V+E) + 堆和 dist O(V)
可套用的代码模板
记住骨架:最小堆按距离取点、能松弛更短才更新并入堆、跳过过期记录。和拓扑 BFS 唯一区别就是「普通队列 → 最小堆」。
Python
# 非负权单源最短路都套这个骨架dist = {src: 0}; heap = [(0, src)]while heap: d, u = heappop(heap) # 取当前最近的点 if d > dist.get(u, INF): continue # 过期记录跳过 for v, w in g[u]: if d + w < dist.get(v, INF): # 能松弛得更短 dist[v] = d + w; heappush(heap, (dist[v], v))易错点
面试追问把动画讲成自己的话
追问核心思路是什么?
追问Dijkstra 为什么要求边权非负?
追问为什么用「懒删除」而不真的删堆里旧记录?
追问复杂度多少?
这道题到这就讲完了。动画和文字是同一套思路——别光看,关掉页面自己默写一遍。然后顺着主线继续:
在水位上升的泳池中游泳
LeetCode 778 · 困难 · 沿着 高级图 继续往下推进
把这道题真正学会,再走
图解算法年卡 ¥99 /年
- ✓本题每步动画的吴师兄语音讲解(全站陆续覆盖)
- ✓小欧带 8 步通关训练——追问到不看答案也能写对、能 30 秒讲给面试官听
- ✓学习报告,记录每道题的掌握程度
76k+ GitHub Star · 吴师兄开源图解算法,几十万开发者在看的算法讲解
想成体系刷透这类套路?去图解算法专题