1. 程式人生 > >最短路徑(dijkstra 與 Floyd)

最短路徑(dijkstra 與 Floyd)

[TOC] ## 1. 如何建圖? > 要跑最短路,首先要有圖 ——~~魯迅~~ 常用的儲存方法有兩種,分別是[鄰接矩陣](https://baike.baidu.com/item/鄰接矩陣/9796080)(用二維陣列表示邊)和[鄰接表](https://baike.baidu.com/item/鄰接表)(模擬連結串列表示邊)兩種,他們各有不同的優勢和不足: | | 鄰接矩陣 | 鄰接表 | | ------------ | -------------- | ------------------------------ | | **使用範圍** | 稠密圖 | 主要是稀疏圖 | | **空間耗費** | n^2(n節點數) | 理論上是 e( e為邊條數) | | **實現方式** | 二維陣列 | 儲存每個節點相連的節點和邊權值 | 通常來講,在資料範圍足夠小時,我們採用鄰接矩陣,而資料範圍大時採用鄰接表 鄰接矩陣實現: 無權圖: ```python g[x][y]=1 #g[x][y]=1表示x到y有一條邊連線 #g[y][x]=1 #去掉註釋後是無向圖 ``` 帶權圖: ```python 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.vid