一、帶權有向圖
二、算法原理
1)由於我們的節點是從1-6,所以我們創建的列表或數組都是n+1的長度,index=0的部分不使用,循環范圍為1-6(方便計算)。
2)循環之前,我們先初始化dis數組和mark數組:
dis數組中保存我們需要求的開始點(start),到其余所有點的最短路徑。初始化的時候,只初始化到自己能夠直接到的節點的距離,不能直接到的距離初始化為max_int(即sys.maxsize)。
mark保存節點的狀態,如果已經被計算過,則狀態為True,還未被計算過,則為False
3)開始循環,注意,我們只循環[1,n]的范圍。index==0不納入循環。
4)N==1時,找到所有Dis元素中,對應mark元素為False的元素。找出其中最小值為10,對應的index為3,也就是節點3。然后在weight數組中,找到3能直接到的節點(且對應mark也要為False),這里找到3能直接到4號節點,且權重為50。此時判斷dis[3]+50<dis[4],如果成立,則使用dis[3]+50更新dis[4]。由於dis[4]等於max_int,所以dis[4]被更新為60。
5)N==2時,找到所有Dis元素中,對應mark元素為False的元素。找出其中最小值為30,對應節點5。然后在weight數組中,找到5能直接到的節點(且對應mark也要為False),為4號和6號節點,且權重為20和60。此時判斷dis[5]+20<dis[4],結果成立,所以dis[4]更新為50。同理dis[6]被更新為90。
6)N==3時,找到所有Dis元素中,對應mark元素為False的元素。找出其中最小值為50,對應節點4。然后在weight數組中,找到4能直接到的節點(且對應mark也要為False),為6號節點,且權重為10。此時判斷dis[4]+10<dis[6],結果成立,所以dis[6]更新為60。
7)N==4時,找到所有Dis元素中,對應mark元素為False的元素。找出其中最小值為60,對應節點6。然后在weight數組中,找到6能直接到的節點(且對應mark也要為False),結果找不到6能直接到的節點。
8)N==5時,找到所有Dis元素中,對應mark元素為False的元素。找出其中最小值為max_int,對應節點2。然后在weight數組中,找到2能直接到的節點(且對應mark也要為False),為3號節點,且權重為5。此時判斷dis[4]+10<dis[6],結果不成立,不成立則不更新dis[3]。
9)N==6時,已經找到到對應mark為False的元素,直接break出循環。整個計算最短路徑的過程結束。
10)可以看到N==6時得到Dis數組的結果是:[max_int,0,max_int,10,50,30,60]。除去index==0的元素,從1-6的元素,即是節點1到所有元素對應的距離(1到2的距離為max_int,說明沒有路線)。
三、Python實現
import numpy as np import sys def dijkstra(start, graph_struct, node): """ function:dijkstra args: start 要計算的起始點 graph_struct 帶權有向圖的結構 node 圖中節點個數 return: dis 元素為-1時,表示沒有路徑。其余為距離 """ # n表示有N個點,m表示M條邊,x表示求那個點到所有點的最短路徑 n, m, x = node, len(graph_struct), start max_int = sys.maxsize weight = np.full([n + 1, n + 1], -1) dis = np.full(n + 1, max_int) # 初始化權重數組,自己到自己為0.其他為暫為-1 for i in range(1, n + 1): weight[i][i] = 0 for i in graph_struct: # 所有存在邊的位置填上權重,沒有關系的位置保持為-1,表示不可直接到達 weight[i[0]][i[1]] = i[2] # 如果是與我們要求的x點相關的點,則也將x到i的權重填入dis列表中 if i[0] == x: dis[i[1]] = i[2] # 程序走到這里,我們就有了權重數組 以及 dis數組(x到各個點的距離,如果沒有邊,則為max_int) # dis : [max_int, 0, max_int, 10, max_int, 30, 100], dis[0]不納入計算,為了方便,我們只考慮index>=1的部分 # 定義內部search函數,開始計算x到所有點的最短路徑,最終更新到dis中 def search(x, dis, weight, n): """ function:search args: x 要求的點 dis 距離數組 weight 權重數組 n 節點的個數 return:dis """ mark = np.full(n + 1, False) # 創建一個mark數組,元素個數為n+1,[1,n]表示1-n點是否被當成最小值加過,已經加過為True,未被加過為False mark[x] = True # 要求的點x,直接標記為加過 dis[x] = 0 # 自己到自己的距離為0 count = 1 # 當前已經加了幾個點,當前只有x點,所以初始化為1 # 開始循環,當count<=n時,說明還有點未被加過 while count <= n: locate = 0 # locate記錄計算出來馬上要被加的點 min = max_int # 用於求最小值時比較用 # 找到dis里面,還沒有加過的位置(mark[idx]=False)里面數的最小值對應的index。 # dis : [9223372036854775807 0 9223372036854775807 10 9223372036854775807 30 100] # mark : [False,True,False,False,False,False],從中找出10的index為 3 # 該for循環完畢后,min中的值就是最小值10 for i in range(1, n + 1): if not mark[i] and dis[i] < min: min = dis[i] locate = i # 如果locate為0,則說明所有點都被加完了,直接退出循環 if locate == 0: break # 如果locate不為0,說明找到了需要加的點,先對其進行標記 mark[locate] = True # 加一個點count增1 count += 1 # 從我們找到的需要加的點locate(例如3)開始,看weight數組中他到各個點的距離 for i in range(1, n + 1): # 如果某個點已經被加過了,我們就不計算locate到這個點的距離了 # 如果locate到某個點的距離為-1,說明沒有路,也不計算 # 條件3:x到locate的距離(dis[locate]) + locate到點i的距離(weight[locate][i]) < x到i的距離 才能更新 if not mark[i] and weight[locate][i] != -1 and ( dis[locate] + weight[locate][i] < dis[i]): # 條件都滿足,則計算,並更新dis中x-->i的距離 dis[i] = dis[locate] + weight[locate][i] return dis # 調用search開始計算x到各個點的距離,記錄到dis數組中 dis = search(x, dis, weight, n) # 打印dis數組 for i in range(1, len(dis)): if dis[i] == max_int: dis[i] = -1 print("%s點到%s點 %s" % (x, i, "的最短路徑為%s" % dis[i] if dis[i] != max_int else '沒有路')) # 返回 return dis if __name__ == '__main__': # 列舉所有的邊的權重,並寫入weight列表 weight_init = [(1, 3, 10), (1, 5, 30), (1, 6, 100), (2, 3, 5), (3, 4, 50), (4, 6, 10), (5, 6, 60), (5, 4, 20)] dis = dijkstra(1, weight_init, 6)
### Dijkstra算法原理還是比較簡單的,但是使用代碼實現的時候比較繞。需要多加復習,熟記於心。