关于Dijkstra算法,有两个比较重要的点:
第一是递归
第二是父节点
通过递归可以用比较少的代码量实现算法本体(就几行而已),同时在递归结束后使用父节点逆推即可实现路线的回溯。
本代码逻辑为通过递归求得每一个节点离原点的最短路径长度,并使用父节点回推从而得到逆序路线,反转后即可输出。相关代码注释已标记
详细的算法逻辑参考其他解说和视频,以下代码是整个功能的实现,仅供参考,有任何问题欢迎评论和指正!
(注意:
输入为邻接矩阵(有向有权图),不可输入负权值,按步骤输入,不可非正常输入。
输出为最短路径长度以及最短路线。
import numpy as np
# ##########获取图、设置图########## #
n = int(input("输入点的个数:")) # 获取图的点个数
adjacent = np.zeros((n, n)) # 预生成一个长和宽为n,名为adjacent的邻接矩阵
best_result = np.zeros((n, n)) # 预生成一个长和宽为n,表示最优解路径的邻接矩阵
close = np.full(n, 999999) # 预生成一个存储每个节点到原点的最近距离的数组,原点默认距离为0
father = np.full(n, -1) # 预生成一个存储每个节点的父节点的数组,原点的父节点默认为-1
# ##########设置邻接矩阵########## #
print("输入邻接矩阵,一个回车输入一个数字,到自身距离为0,没有直接连接距离也为0!") # 自定义输入邻接矩阵
for i in range(n):
for j in range(n):
adjacent[i][j] = int(input("第" + str(i + 1) + "行第" + str(j + 1) + "个数:"))
# adjacent = [[0, 2, 0, 9, 6, 0, 0, 0, 0], # 备用邻接矩阵
# [0, 0, 1, 0, 3, 0, 0, 0, 0],
# [0, 0, 0, 0, 1, 0, 6, 0, 0],
# [0, 0, 0, 0, 0, 0, 0, 4, 0],
# [0, 0, 0, 2, 0, 9, 0, 7, 0],
# [0, 0, 0, 0, 0, 0, 5, 0, 1],
# [0, 0, 0, 0, 0, 0, 0, 0, 5],
# [0, 0, 0, 0, 0, 1, 0, 0, 5],
# [0, 0, 0, 0, 0, 0, 0, 0, 0]]
# ##########设置最短距离数组########## #
start = int(input("输入出发点的序号:")) - 1 # 从数组序号为start的起始点开始寻找最优路径(数组序号比图序号小1)
stop = int(input("输入目的点的序号:")) - 1 # 以finish为路径终点
close[start] = 0 # 原点到原点的距离为0
# ##########dijkstra算法本体########## #
def dijkstra_recursion(start1): # 运行一次函数完成一个点周围的点的最短路径运算,如果没有最短路径更新,不会触发下一次递归
for k in range(n):
if close[start1] + adjacent[start1][k] < close[k] and adjacent[start1][k] != 0: # 存在点k能连接到点start1且能使得最短距离更短
close[k] = close[start1] + adjacent[start1][k] # 更新原点经过点start1到点k的最短距离
father[k] = start1 # 更新点k的父节点为start1
start1 = k # 让点k变为下一次递归的起始点
dijkstra_recursion(start1) # 让点k继续递归
# ##########处理算法执行结果########## #
dijkstra_recursion(start) # 从起始点start开始递归
if close[stop] == 999999: # 如果起始点没法到达目的点(即目的点到起始点距离为999999没变)
print("该起始点无法到达目的点!")
elif start == stop:
print("起始点与目的点重合!") # 如果起始点与目的点重合(不考虑自环,因为会增加路径长度)
else:
print("\n路径长度为:", close[stop])
route = np.full(n, -1) # 用来存储通过父节点得出的路线
for i in range(n):
route[n - i - 1] = stop # 路线数组的最后一个位置是目的点
stop = father[stop] # 将stop变为改点的父节点,继续往回推
if father[stop] == -1: # 如果下一个点的父节点是-1,那么当前就已经到出发点了,退出循环
break
print("最优路径为:\n", start + 1, end="") # 从原点开始输出路径,不换行
for i in range(n):
if route[i] != -1: # 循环至原点开始输出(不包含原点),不换行
print("->", route[i] + 1, end="")