最短路徑(dijkstra 與 Floyd)

1. 如何建圖?

要跑最短路,首先要有圖 ——魯迅html

經常使用的存儲方法有兩種,分別是鄰接矩陣(用二維數組表示邊)和鄰接表(模擬鏈表表示邊)兩種,他們各有不一樣的優點和不足:python

鄰接矩陣 鄰接表
使用範圍 稠密圖 主要是稀疏圖
空間耗費 n^2(n節點數) 理論上是 e( e爲邊條數)
實現方式 二維數組 存儲每一個節點相連的節點和邊權值

一般來說,在數據範圍足夠小時,咱們採用鄰接矩陣,而數據範圍大時採用鄰接表算法

鄰接矩陣實現:數組

無權圖:ide

g[x][y]=1 #g[x][y]=1表示x到y有一條邊鏈接
#g[y][x]=1 #去掉註釋後是無向圖

帶權圖:post

g[x][y]=w #g[x][y]=w表示x到y有一條權值爲w的邊
#g[y][x]=w #去掉註釋後是無向圖

2. Floyd

多源最短路算法,用鄰接矩陣存儲,能夠處理負邊權,但不能處理負環測試

多源最短路就是說只要跑一次,任意兩點的最短路都能求啦( ̄︶ ̄),而其餘單源最短路跑一次只能得出一個點到其餘點的最短路ui

用鄰接矩陣存最短路( dis[i][j]表示 ij的最短距離)開一個三重循環(!)code

外層枚舉中間點,中間枚舉起點,內層枚舉終點,當三個點互不相同時進行鬆弛操做,若是通過中間點以後的路程和比原路程短,就更新距離,一輪事後,咱們獲得了一個新的矩陣,而後咱們把中間點換成下一個點,再次鬆弛,的到一個新的矩陣,執行 n 次以後,第 n個矩陣就是咱們的答案啦orm

#提早將鄰接矩陣存在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:

http://www.javashuo.com/article/p-zcecsmme-no.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

相關文章
相關標籤/搜索