1. 如何建圖?
要跑最短路,首先要有圖 ——
魯迅
常用的儲存方法有兩種,分別是鄰接矩陣(用二維陣列表示邊)和鄰接表(模擬連結串列表示邊)兩種,他們各有不同的優勢和不足:
鄰接矩陣 | 鄰接表 | |
---|---|---|
使用範圍 | 稠密圖 | 主要是稀疏圖 |
空間耗費 | n^2(n節點數) | 理論上是 e( e為邊條數) |
實現方式 | 二維陣列 | 儲存每個節點相連的節點和邊權值 |
通常來講,在資料範圍足夠小時,我們採用鄰接矩陣,而資料範圍大時採用鄰接表
鄰接矩陣實現:
無權圖:
g[x][y]=1 #g[x][y]=1表示x到y有一條邊連線
#g[y][x]=1 #去掉註釋後是無向圖
帶權圖:
g[x][y]=w #g[x][y]=w表示x到y有一條權值為w的邊
#g[y][x]=w #去掉註釋後是無向圖
2. Floyd
多源最短路演算法,用鄰接矩陣儲存,可以處理負邊權,但不能處理負環
多源最短路就是說只要跑一次,任意兩點的最短路都能求啦( ̄︶ ̄),而其他單源最短路跑一次只能得出一個點到其他點的最短路
用鄰接矩陣存最短路( dis[i][j]
表示 i
到j
的最短距離)開一個三重迴圈(!)
外層列舉中間點,中間列舉起點,內層列舉終點,當三個點互不相同時進行鬆弛操作,如果經過中間點之後的路程和比原路程短,就更新距離,一輪過後,我們得到了一個新的矩陣,然後我們把中間點換成下一個點,再次鬆弛,的到一個新的矩陣,執行 n
次之後,第 n
個矩陣就是我們的答案啦
#提前將鄰接矩陣存在dis陣列裡,其他不連通的地方初始化成無窮大
for k in range(n): #列舉中間點
for i in range(n): #列舉起點
if(i!=k): #節省時間,如果一樣就不往下走
for j in range(n): #列舉終點
if(i!=j and j!=k): #繼續判斷,如果有一樣的就不往下走
dis[i][j] = min(dis[i][j],dis[i][k]+dis[k][j])
3. Dijkstra
單源最短路徑
將所有節點分成兩類:已確定從起點到當前點的最短路長度的節點,以及未確定從起點到當前點的最短路長度的節點(下面簡稱「未確定節點」和「已確定節點」)。
每次從「未確定節點」中取一個與起點距離最短的點,將它歸類為「已確定節點」,並用它「更新」從起點到其他所有「未確定節點」的距離。直到所有點都被歸類為「已確定節點」。
用節點 A「更新」節點 B 的意思是,用起點到節點 A 的最短路長度加上從節點 A 到節點 B 的邊的長度,去比較起點到節點 B 的最短路長度,如果前者小於後者,就用前者更新後者。這種操作也被叫做「鬆弛」。
這裡暗含的資訊是:每次選擇「未確定節點」時,起點到它的最短路徑的長度可以被確定。
——力扣(LeetCode)
def dijkstra(u):
#初始化起點 u 到每一個點的距離
for k in range(n):
dis[k] = g[u][k]
print("起點到其他節點的初始距離:",dis)
used[u] = 1
for k in range(n):
minv = float('inf')
#在未確定節點中找與起點距離最短的
for i in range(n):
if not used[i] and dis[i] < minv:
minv = dis[i]
temp = i
#中轉位置, 變為已確定節點,更新距離
used[temp] = 1
for i in range(n):
if g[temp][i]+dis[temp] < dis[i]:
dis[i] = g[temp][i]+dis[temp]
print("第 {} 次迭代,起點到其他節點的距離:{}".format(k,dis))
測試下
with open("path.txt",'r') as f:
n = int(f.readline())
weights = f.readlines()
print("頂點數:",n)
dis = [0]*n
used = [0]*n
# 定義鄰接矩陣儲存圖
g = [[0]*n for _ in range(n)]
for u in range(n):
for v in range(n):
g[u][v] = int(weights[u].strip().split()[v])
print("鄰接矩陣:",g)
# g[u][v] = g[v][u] = w # 建立圖,節點間沒有連線賦值為 無窮大
dijkstra(0)
執行結果:
起點到其他節點的初始距離: [65535, 33, 21, 26, 65535, 65535, 65535, 65535]
第 0 次迭代,起點到其他節點的距離:[42, 33, 21, 26, 65535, 42, 65535, 65535]
第 1 次迭代,起點到其他節點的距離:[42, 33, 21, 26, 62, 42, 96, 117]
第 2 次迭代,起點到其他節點的距離:[42, 33, 21, 26, 62, 42, 96, 117]
第 3 次迭代,起點到其他節點的距離:[42, 33, 21, 26, 62, 42, 71, 117]
第 4 次迭代,起點到其他節點的距離:[42, 33, 21, 26, 62, 42, 71, 117]
第 5 次迭代,起點到其他節點的距離:[42, 33, 21, 26, 62, 42, 71, 117]
第 6 次迭代,起點到其他節點的距離:[42, 33, 21, 26, 62, 42, 71, 117]
第 7 次迭代,起點到其他節點的距離:[42, 33, 21, 26, 62, 42, 71, 117]
附上資料
8
65535 33 21 26 65535 65535 65535 65535 65535
33 0 65535 27 30 65535 65535 65535 65535
21 65535 0 22 65535 21 65535 65535 65535
26 27 22 019 36 33 70 91
65535 30 65535 190 65535 41 64 80
65535 65535 21 36 65535 0 29 65535 65535
65535 65535 65535 33 41 290 22 65535
65535 65535 65535 70 64 65535 22 0 42
references:
https://www.cnblogs.com/Staceyacm/p/10782094.html
https://www.luogu.com.cn/blog/FrozaFerrari/xue-tu-lun-ni-zhen-di-liao-xie-zui-duan-lu-ma-post
https://www.bilibili.com/video/BV1q4411M7r9/?spm_id_from=333.788.videocard.1