Python 实现 Dijkstra 最短路径算法:矩阵与字典两种方案
本文介绍如何使用 Python 实现 Dijkstra 最短路径算法。通过邻接矩阵和字典两种数据结构,分别演示了如何计算单源到所有节点的最短路径。文章包含完整的代码示例,对比了矩阵与字典在空间占用和实现逻辑上的差异,并详细解析了路径回溯与距离更新的核心步骤。
算法核心逻辑与数据结构选择
Dijkstra 算法用于计算图中一个节点到其余所有节点的最短路径。在 Python 中,图的存储通常有两种方式:邻接矩阵和邻接字典(或邻接表)。邻接矩阵适合节点密集且数量较少的场景,而字典实现则在处理稀疏图时能显著节省空间。
算法的基本流程包括:初始化所有节点的距离为无穷大(源点为 0),每次从未处理的节点中选取距离最小的节点,更新其邻居节点的距离,并记录路径的前驱节点。当所有节点都被处理后,即可通过前驱节点反向回溯得到完整路径。

基于邻接矩阵的实现
使用二维列表(矩阵)存储图结构时,索引直接对应节点编号。这种方法直观,但需要预先定义节点数量,且对于稀疏图会浪费大量空间存储无效连接(通常用无穷大表示)。
在代码实现中,我们定义了一个无穷大常量 inf(示例中为 999999)来表示不可达。算法维护两个关键数组:path_array 记录当前已知的最短距离,path_parent 记录到达该节点的前驱节点,用于后续路径重构。
_=inf=999999#inf
def Dijkstra_all_minpath(start,matrix):
length=len(matrix)#该图的节点数
path_array=[]
temp_array=[]
path_array.extend(matrix[start])#深复制
temp_array.extend(matrix[start])#深复制
temp_array[start] = inf#临时数组会把处理过的节点的值变成inf,表示不是最小权值的节点了
already_traversal=[start]#start已处理
path_parent=[start]*length#用于画路径,记录此路径中该节点的父节点
while(len(already_traversal)<length):
i= temp_array.index(min(temp_array))#找最小权值的节点的坐标
temp_array[i]=inf
path=[]#用于画路径
path.append(str(i))
k=i
while(path_parent[k]!=start):#找该节点的父节点添加到path,直到父节点是start
path.append(str(path_parent[k]))
k=path_parent[k]
path.append(str(start))
path.reverse()#path反序产生路径
print(str(i)+':','->'.join(path))#打印路径
already_traversal.append(i)#该索引已经处理了
for j in range(length):#这个不用多说了吧
if j not in already_traversal:
if (path_array[i]+matrix[i][j])<path_array[j]:
path_array[j] = temp_array[j] =path_array[i]+matrix[i][j]
path_parent[j]=i#说明父节点是i
return path_array
#领接矩阵
adjacency_matrix=[[0,10,_,30,100],
[10,0,50,_,_],
[_,50,0,20,10],
[30,_,20,0,60],
[100,_,10,60,0]
]
print(Dijkstra_all_minpath(4,adjacency_matrix))矩阵实现要点
- 距离更新:遍历所有未处理节点,若通过当前最小节点
i到达节点j的距离更短,则更新path_array[j]和path_parent[j]。 - 路径回溯:通过
path_parent数组从目标节点反向追踪至起点,生成路径字符串。 - 输出格式:直接打印节点及其对应的最短路径,并返回所有节点的距离列表。
基于字典的优化实现
对于节点数量不确定或图结构稀疏的情况,使用字典(嵌套字典)存储图结构更为灵活。字典实现避免了预先分配大量空间,且能更清晰地表达节点间的连接关系。
在字典实现中,path_graph 是一个字典,键为节点,值为当前最短距离。算法逻辑与矩阵实现类似,但遍历邻居节点时,需要检查字典中是否存在该连接。
def Dijkstra_all_minpath_for_graph(start,graph):
inf = 999999 # inf
length=len(graph)
path_graph={k:inf for k in graph.keys()}
already_traversal=set()
path_graph[start]=0
min_node=start#初始化最小权值点
already_traversal.add(min_node)#把找到的最小节点添加进去
path_parent={k:start for k in graph.keys()}
while(len(already_traversal)<=length):
p = min_node
if p!=start:
path = []
path.append(str(p))
while (path_parent[p] != start):#找该节点的父节点添加到path,直到父节点是start
path.append(str(path_parent[p]))
p=path_parent[p]
path.append(str(start))
path.reverse()#反序
print(str(min_node) + ':', '->'.join(path))#打印
if(len(already_traversal)==length):break
for k in path_graph.keys():#更新距离
if k not in already_traversal:
if k in graph[min_node].keys() and (path_graph[min_node]+graph[min_node][k])<path_graph[k]:
path_graph[k]=path_graph[min_node]+graph[min_node][k]
path_parent[k]=min_node
min_value=inf
for k in path_graph.keys():#找最小节点
if k not in already_traversal:
if path_graph[k]<min_value:
min_node=k
min_value=path_graph[k]
already_traversal.add(min_node)#把找到最小节点添加进去
return path_graph
adjacency_graph={0:{1:10,3:30,4:100},
1:{0:10,2:50},
2:{1:50,3:20,4:10},
3:{0:30,2:20,4:60},
4:{0:100,2:10,3:60}}
print(Dijkstra_all_minpath_for_graph(4,adjacency_graph))字典实现要点
- 动态更新:通过
path_graph.keys()遍历所有节点,检查当前最小节点min_node的邻居字典中是否存在该节点。 - 空间效率:仅存储实际存在的边,对于稀疏图,空间复杂度从
O(V^2)降低到O(V+E)。 - 输出格式:返回一个字典,键为节点,值为最短距离,便于后续程序处理。
两种实现对比
矩阵实现适合节点数量固定且较少的场景,代码逻辑直接,但空间利用率低。字典实现则更加灵活,适合节点数量动态变化或图结构稀疏的场景,空间效率更高。在实际应用中,应根据具体需求选择合适的存储结构。
无论使用哪种方式,Dijkstra 算法的核心在于不断选取当前距离最小的未处理节点,并更新其邻居的距离。通过记录前驱节点,可以高效地回溯出完整路径。
以上就是 Python 矩阵与字典实现最短路径算法的详细内容,更多关于 Python 算法、Dijkstra 算法、邻接矩阵和字典实现的相关资料请关注本站其它相关文章!
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









