Dijkstra算法不支持负权边,结果错误且不报错;应改用Bellman-Ford或Johnson;手写时须用visited避免重复松弛;networkx调用需指定weight参数;优先使用heapq而非PriorityQueue;需提前检查节点存在性。

Python里用dijkstra前先确认图有没有负权边
直接说结论:dijkstra算法在Python中(比如用networkx.shortest_path或手写)不支持负权边,一旦有,结果大概率错,而且不会报错——它会安静地返回一个看似合理、实则错误的路径。
常见错误现象是:明明存在一条经过负权边的更短路径,但dijkstra返回的路径长度更大;或者不同起点反复运行,路径长度不满足三角不等式。
- 负权边存在时,改用
bellman_ford(networkx里有现成函数)或johnson - 检查图:遍历所有边,看
edge[2].get('weight', 1) < 0是否成立(假设用networkx.Graph或DiGraph) - 如果只是有负权环,
bellman_ford会抛出networkx.NetworkXUnbounded异常,这是唯一靠谱的提示
用heapq手写dijkstra时别漏掉“已访问”判断
很多人照着伪代码写,只维护一个优先队列,却忘了跳过已确定最短距离的节点,导致重复松弛、时间爆炸,甚至逻辑错误。
典型表现是:小图跑几秒不出结果,或者dist[v]被反复更新,最终dist数组值比预期大。
立即学习“Python免费学习笔记(深入)”;
- 必须用一个集合或布尔列表
visited记录哪些节点已出队并确定最短距 - 每次
heappop后先检查if node in visited: continue,再处理邻接点 -
heapq里存的是(dist[node], node),但dist[node]可能已被更新过,所以不能仅靠堆顶值判断是否有效
networkx.dijkstra_path和dijkstra_path_length的权重字段名必须匹配
如果你的图边属性不是'weight',比如存的是'cost'或'time',直接调用会默认按1算权重,路径就变成BFS了。
使用场景:读取gml、graphml或手动加边时自定义了字段名。
- 显式传参:
networkx.dijkstra_path(G, src, dst, weight='cost') - 如果边没设
weight属性,默认值是1,不是报错——这点容易被忽略 - 用
G.edges(data=True)快速检查前几条边,确认字段名是否存在、类型是否为数值(字符串"5"会导致TypeError)
稀疏图用heapq,稠密图考虑queue.PriorityQueue?不,别换
有人听说PriorityQueue线程安全就以为它更适合,其实完全没必要。Python标准库的heapq是底层C实现,比PriorityQueue快得多,且PriorityQueue只是heapq的封装,还多了锁开销。
性能影响明显:10万节点、百万边的图,用PriorityQueue可能慢2–3倍;而真正瓶颈从来不在队列,而在邻接表遍历和字典查找。
- 坚持用
heapq,配合list模拟堆,别碰PriorityQueue - 邻接表用
defaultdict(list)或dict,别用nx.to_dict_of_dicts转两次——那会吃内存 - 如果真卡性能,优先检查是否重复建图、是否用了
nx.path_graph这类高开销构造函数
dijkstra会直接抛networkx.NodeNotFound或返回空路径,得提前if src not in G or dst not in G兜底。










