Dijkstra算法与Floyd算法分别用于求单源最短路径与多源最短路径,其中Dijkstra算法要求图中所有边的权值为正,而Floyd算法只要求不存在负权环就行(即可以存在负权的边,但是若有环,环的权值和不能为负)。
本文在给出这两个算法的求解代码的同时,给出各自求解路径的代码。
Dijkstra算法
Dijkstra算法是一个贪心算法,其思想是,设置一个从源点到顶点最短距离已知的点的集合S
,然后不断地做贪心选择来扩充这个集合。最初时,S
仅包含源点,从剩余的点的集合V-S
(V
是所有顶点的集合)中选择一个距离源点最近的点,把它加入到S
中来,然后以这个点更新距离矩阵,不断重复这个过程,直至S
包含所有的点。
代码如下
def dijkstra(matrix,sp,query):
"""
matrix: 各节点距离矩阵,以无穷代表无连接,N*N
sp : 源点坐标
query : 查询路径的点
"""
N = len(matrix)
# S集合,一开始为空
s = [False for i in range(N)]
# 各点至源点的距离
dist = [0 for i in range(N)]
# 前导节点
prev = [-1 for i in range(N)]
for i in range(N):
dist[i] = matrix[sp][i]
if dist[i] == float('inf'):
prev[i] = -1
else:
prev[i] = sp
# 把源点加入集合S中
prev[sp] = -1
s[sp] = True
for i in range(N):
# 从源点出发,找V-S中距离源点最近的点
u = sp
temp = float('inf')
# 每次更新V-S中最近的距离的点
for j in range(N):
if not s[j] and dist[j] < temp:
u = j
temp = dist[j]
# 把找到的点加入S集合中
s[u] = True
# 依据新找到的点,更新距离
for j in range(N):
# 如果点j在V-S中,且与u有连接
if not s[j] and matrix[u][j] < float('inf'):
# 从源点出发,经过u到j的距离
new_dist = dist[u] + matrix[u][j]
# 如果这个距离比原先的距离小,则更新距离,并更新前导节点
if new_dist < dist[j]:
dist[j] = new_dist
prev[j] = u
# 寻找路径
# 实际上,路径记录在prev中,假设要找起点至某个点j的路径,只需不断回溯prev[j]就行,直至其等于源点坐标
route = []
dst = query
while dst != sp:
route.append(dst)
dst = prev[dst]
route.append(sp)
route.reverse()
# 打印路径
print('从源点至%d点的路径为:'%query)
for i in range(len(route)-1):
print('%d -> '%route[i],end='')
print(route[-1])
return dist,prev
Floyd算法
Floyd算法用于求多源最短路径,即各个点之间的最短路径,给定初始距离矩阵,其思想是从i
点到j
的距离,是否可以通过若干个中间点变得更短,如果可以,则更新距离。具体过程可参考这里。
同样地,可以在每次更新距离时记录前导节点,在寻找路径时,需要不断回溯。
代码如下:
def floyd(matrix,query):
"""
matrix: 初始距离矩阵
query : 查询从i点到j点的路径
"""
N = len(matrix)
# 前导节点矩阵,-1表示i与j之间没有通过中间点连接
prev = [[-1 for j in range(N)] for i in range(N)]
# k是中间点
for k in range(N):
for i in range(N):
for j in range(N):
# 如果通过k连接,距离会变得更短,则更新距离和前导节点
if matrix[i][k] + matrix[k][j] < matrix[i][j]:
matrix[i][j] = matrix[i][k] + matrix[k][j]
prev[i][j] = k
# 寻找路径
# 现在得到的路径中的值是这样的 i -> j 要经过k点,
# 所以假设要求i -> j的路径,先读出path[i][j],如果这个值为-1,表示直接可以到,不需要中转
# 如果这个值为k,再读取path[k][j],如果为-1,则路径是i->k->j,同理,若仍为某个k2,则继续往下
# 直至path中的值是-1
src,dst = query[0],query[1]
route = [src]
while prev[src][dst] != -1:
src = prev[src][dst]
route.append(src)
route.append(dst)
print('从%d到%d的路径为:'%(query[0],query[1]))
for i in range(len(route)-1):
print('%d -> '%route[i],end='')
print(route[-1])