图解题库 / 图 · Dijkstra

743. 网络延迟时间

中等 含交互动画

学完这道你应该能: 说清它为什么用「Dijkstra」,而不是死记步骤; 合上页面,自己默写出核心代码; 用 30 秒把思路讲清楚。

题目描述

有向带权图,从节点 k 发信号,求传到所有节点的最短用时(取最大的那个)。

edges = 2→1(1), 2→3(1), 3→4(1)
k = 2
输出 = 2

思路解析

dist[源]=0,其他=∞。用最小堆,每次弹出「距离最小且没确定」的点,它的距离就定了,再用它去更新(松弛)邻居的距离。

拓扑排序用普通队列(谁先来谁先出);带权最短路必须用最小堆,每次取「当前距离最小」的点,不然就算错了。

建图 · 源点 2=0:源点 2 的 dist=0,放进最小堆。边上的数字是权重(用时)。

弹出 2 · 松弛邻居:弹出距离最小的 2(dist=0),敲定。沿着它的边松弛:dist[1]=0+1=1,dist[3]=0+1=1,都入堆。

弹出 1:弹出 1(dist=1),敲定。它没有出边,不用松弛谁。

弹出 3 · 松弛 4:弹出 3(dist=1),敲定。松弛它的邻居 4:dist[4] = dist[3]+1 = 2,然后把 (2, 4) 压入最小堆(heappush)。右侧堆里就出现了 (2,4)。

弹出 4 · 全部确定:弹出 4(dist=2),堆空了,所有点的最短距离都确定了。

答案 = 最大 dist:信号传遍全网的时间 = 所有最短距离里的最大值 = 2。(如果有某个点是 ∞,说明传不到,返回 −1)

和拓扑的 BFS 区别在于:带权图要用最小堆按「距离」取点,而不是按入度。

参考代码(Python)

Python
1g = defaultdict(list)
2for a,b,w in times: g[a].append((b,w))    # 带权邻接表
3dist = {k: 0}; heap = [(0, k)]
4while heap:
5    d, u = heappop(heap)                  # 取距离最小的
6    if d > dist.get(u, INF): continue     # 过期的旧记录跳过
7    for v, w in g[u]:
8        if d + w < dist.get(v, INF):      # 能松弛得更短
9            dist[v] = d + w; heappush(heap, (dist[v], v))
10return max(dist.values()) if len(dist)==n else -1

复杂度分析

  • 时间复杂度:O(E·logV) —— 每条边可能入堆一次
  • 空间复杂度:O(V+E) —— 邻接表 + 堆 + dist

套路模板

记住骨架:最小堆按距离取点、能松弛更短就更新并入堆、跳过过期记录。和拓扑 BFS 唯一区别就是「普通队列 → 最小堆」。

Python
1# 非负权单源最短路都套
2dist = {src: 0}; heap = [(0, src)]
3while heap:
4    d, u = heappop(heap)               # 取当前最近的点
5    if d > dist.get(u, INF): continue  # 过期记录跳过
6    for v, w in g[u]:
7        if d + w < dist.get(v, INF):   # 能松弛得更短
8            dist[v] = d + w; heappush(heap, (dist[v], v))

易错点

  • 用普通队列(FIFO) → ✅ 用最小堆(优先队列)按距离取(带权图必须先处理距离最近的点)
  • 不跳过过期记录 → ✅ if d > dist[u]: continue(同一点可能多次入堆,旧的要跳过)
  • 忘了判断不可达 → ✅ 最后若有点没被访问,返回 -1(有些点信号传不到)

以上文字与上方动画完全同一思路:先看动画建立直觉 → 读文字巩固 → 关掉页面自己默写一遍代码,能写出来才算真的掌握

下一题 →49. 字母异位词分组 ← 返回题库