PrepStack · Markdown HTML

Dijkstra 非负权最短路

有序前沿与路径 · HTML

数学 / 逻辑形态

dist[s]=0;对边 u→v,w≥0 做 relaxation:cand=dist[u]+w。堆弹出的非过期最小标签即为最终最短距离。

识别信号

容易误判的相似信号

SignalWhy it is different
存在负权边已弹出标签仍可能被负边改善,Dijkstra 的贪心证明失效。
路径最大值最小聚合器不是加法;应改用 minimax-dijkstra 的 max relaxation。

核心不变量

每次弹出的非过期标签 (d,u) 是所有未确定路径标签中的最小值;非负边保证任何绕行都不能把 u 降到 d 以下。

状态、转移与处理顺序

ItemDefinition
Statedist[v] 暂定最短和、最小堆中的 (distance,node),以及可选 parent。
Transition对 u→v,w 计算 nd=d+w;若 nd<dist[v],更新 dist 并把新标签压堆。
Frontier / order按暂定距离排序的 min-heap;Python 用重复入堆 + lazy deletion 代替 decrease-key。

Python 骨架

import heapq

def dijkstra(graph, start, goal=None):
    dist = {start: 0}
    heap = [(0, start)]
    while heap:
        d, u = heapq.heappop(heap)
        if d != dist.get(u):
            continue
        if goal is not None and u == goal:
            return d
        for v, w in graph.get(u, []):
            if w < 0:
                raise ValueError('negative edge')
            nd = d + w
            if nd < dist.get(v, float('inf')):
                dist[v] = nd
                heapq.heappush(heap, (nd, v))
    return dist if goal is None else float('inf')

正确性思路

  1. 设 u 是首次弹出的非过期最小标签;若存在更短路径,取该路径上第一个未确定点,其前驱已确定并应产生更小候选,矛盾。
  2. relaxation 枚举每条可能成为更优前缀的边,因此不会错过最短路径。
  3. 过期堆项不等于当前 dist 时跳过,不改变任何仍可能最优的标签。

复杂度

TimeSpaceParameters
O((V+E) log H),通常写 O(E log V)O(V+H),lazy heap 最坏 H=O(E)V 为点、E 为边,H 为堆中同时存在的标签数;理论 decrease-key 堆可令 H≤V,Python 重复入堆常写 O(E log E),且简单图中 log E=O(log V)。

边界条件

错误模式

PatternWhy it fails / fix
把起点放进 visited 后又跳过初始化 visited={start} 且 pop 时检查会直接丢失起点。
push 时就最终确定节点可能后来被更短路径改善;最终性只在非过期 pop 时成立。
忘记更新 dist压堆与 dist 赋值必须属于同一次成功 relaxation。

选择与排除规则

相关模板

RelationTemplateBoundary
alternative网格 DP任意方向非负权移动形成环。
structural-analogyK 路归并堆前沿由状态扩展且同一状态可被多路径到达。
改变路径代数Minimax / Bottleneck Dijkstra路径目标由求和改为最小化最大单步成本。

原创微型例子

环节内容
Results 到 a 的最短和为 3。
Setups→a 成本 7,s→b 成本 2,b→a 成本 1。
Trace先压 a:7、b:2;弹 b 后把 a 改善为 3;a:3 先于过期 a:7 弹出。

Recall prompts

我的补充