相關概念
對於一個圖G=(V, E),求圖中兩點u, v間最短路徑長度,稱為圖的最短路徑問題。最短路徑中最長的稱為圖的直徑。
其中,求圖中確定的某兩點的最短路徑算法,稱為單源最短路徑算法。求圖中任意兩點間的最短路徑算法,稱為多源最短路徑算法。
常用的路徑算法有:
- Dijkstra算法
- SPFA算法\Bellman-Ford算法
- Floyd算法\Floyd-Warshall算法
- Johnson算法
其中最經典的是Dijkstra算法和Floyd算法。Floyd算法是多源最短路徑算法,可以直接求出圖中任意兩點間的距離,因此只要取其中最大的就可以得到圖的直徑。
Floyd算法
算法思想
假設Dis(i,j)為節點u到節點v的最短路徑的距離(最短路徑長度),對於每一個節點k,檢查Dis(i,k) + Dis(k,j) < Dis(i,j)是否成立,如果成立,說明從i到k再到j的路徑比i直接到j的路徑短,便記錄Dis(i,j) = Dis(i,k) + Dis(k,j)。因此,當遍歷完所有節點k,Dis(i,j)中記錄的便是i到j的最短路徑的距離。
算法特點
- 使用了動態規划思想
- 可以計算無向圖或有向圖
- 核心代碼簡短(五行)
- 可以一次性計算出任意兩點間的距離
- 算法復雜度O(n^3),是一個好算法
一個關鍵性問題
在判斷Dis(i,k) + Dis(k,j) < Dis(i,j)這個公式時,如果經過k的距離更短就選擇k,但是這能否保證此時Dis(i,k)和Dis(k,j)已經取得了最小值呢?
答案是肯定的,可以用數學歸納法證明,參考這篇博客
示例
待求直徑的圖G
程序輸入
2(表示無向圖)
8 9 (表示8個頂點,9條邊)
1 2 5 (表示頂點1和頂點2之間的距離權重是5)
... ...
程序輸出
(鄰接矩陣,矩陣元素M[i][j]表示頂點Vi與Vj間的距離)
(各個頂點間的最短路徑以及路徑長度,對於此例,頂點V4與V6或V8間的距離都是10,是距離最遠的兩個頂點對)
(此圖的直徑)
Python源代碼
# ----------------------------------------------
# Project: calculate diameter of graph
# Using floyd algorithm
# ----------------------------------------------
# define function: print shortest path
def getPath(i, j):
if i != j:
if path[i][j] == -1:
print('-', j+1, end='')
else:
getPath(i, path[i][j])
getPath(path[i][j], j)
def printPath(i, j):
print(' Path:', i+1, end='')
getPath(i, j)
print()
print('---------------- Program start ----------------')
# read data
flag = input('please input type of graph(1:directed '
'graph; 2:undirected graph): ')
vertex, edge = input('please input the number of '
'vertex and edge: ').strip().split()
# initialized
flag = int(flag)
vertex = int(vertex)
edge = int(edge)
inf = 99999999
dis = [] # matrix of the shortest distance
path = [] # record the shortest path
for i in range(vertex):
dis += [[]]
for j in range(vertex):
if i == j:
dis[i].append(0)
else:
dis[i].append(inf)
for i in range(vertex):
path += [[]]
for j in range(vertex):
path[i].append(-1)
# read weight information
print('please input weight info(v1 v2 w[v1,v2]): ')
for i in range(edge):
u, v, w = input().strip().split()
u, v, w = int(u)-1, int(v)-1, int(w)
if flag == 1:
dis[u][v] = w
elif flag == 2:
dis[u][v] = w
dis[v][u] = w
print('the weight matrix is:')
for i in range(vertex):
for j in range(vertex):
if dis[i][j] != inf:
print('%5d' % dis[i][j], end='')
else:
print('%5s' % '∞', end='')
print()
# floyd algorithm
for k in range(vertex):
for i in range(vertex):
for j in range(vertex):
if dis[i][j] > dis[i][k] + dis[k][j]:
dis[i][j] = dis[i][k] + dis[k][j]
path[i][j] = k
print('===========================================')
# output the result
print('output the result:')
if flag == 1:
for i in range(vertex):
for j in range(vertex):
if (i != j) and (dis[i][j] != inf):
print('v%d ----> v%d tol_weight:'
'%3d' % (i+1, j+1, dis[i][j]))
printPath(i, j)
if (i != j) and (dis[i][j] == inf):
print('v%d ----> v%d tol_weight:'
' ∞' % (i+1, j+1))
printPath(i, j)
if flag == 2:
for i in range(vertex):
for j in range(i+1, vertex):
print('v%d <----> v%d tol_weight:'
'%3d' % (i+1, j+1, dis[i][j]), '', end='')
printPath(i, j)
print()
for i in range(vertex):
for j in range(vertex):
if dis[i][j] == inf:
dis[i][j] = 0
# max(max(dis)): the max item of two dimension matrix
print('>> the diameter of graph: %d <<' % max(max(dis)))
print('-------------- Program end ----------------')
Reference
最短路徑_百度百科
最短路徑—Dijkstra算法和Floyd算法
最短路徑問題---Floyd算法詳解 - CSDN博客
Floyd算法(記錄路徑) - CSDN博客