數據結構複習(python)——狄克斯特拉算法(Dijkstra)

學習材料

《算法圖解》第7章

適用情景

找出從一個節點到另一個節點的最短(快)路徑

準備工作

對一個帶權圖進行描述,可使用兩個散列表(字典),其中一個散列表用來描述每個結點的指向及權值,對於一個節點指向兩個及以上的節點的情況,可使字典嵌套;另一個散列表用來描述從“起點”開始到其他節點的距離,若起點未與某個節點直接相連,在初始時把這段距離置爲無窮大。

舉例:圖及其對應散列表如下

               圖

graph={}
graph["start"]={}
graph["start"]["A"]=5
graph["start"]["B"]=2
graph["A"]={}
graph["A"]["C"]=4
graph["A"]["D"]=2
graph["B"]={}
graph["B"]["A"]=8
graph["B"]["D"]=7
graph["C"]={}
graph["C"]["fin"]=3
graph["C"]["D"]=6
graph["D"]={}
graph["D"]["fin"]=1
graph["fin"]={}
costs={}
costs["A"]=5
costs["B"]=2
costs["C"]=float('inf')
costs["D"]=float('inf')
costs["fin"]=float('inf')

爲了記錄最短路徑,還需要一個散列表描述最短路徑中的父子關係。

parents={}
parents["A"]="start"
parents["B"]="start"
parents["C"]=""
parents["D"]=""
parents["fin"]=""

執行算法時,將不斷更新costs和parents。

算法流程

(1)找出costs中對應值最小的結點,且未被處理過(未在processed中)

(2)檢查該節點的鄰居,尋找是否有從起點開始經過該節點到其鄰居的更短路徑,若有,則更新其鄰居在costs的對應值

(3)重複(1)(2),直到對每個節點都做過這件事

(4)計算最短路徑

代碼如下:

processed=[]

def find_lowest_cost_node(costs):
    lowest_cost=float('inf')
    lowest_cost_node=None
    for node in costs: #遍歷所有節點
        cost=costs[node]
        if cost<lowest_cost and node not in processed:
            #如果當前節點與起點距離的更小,且未被處理過
            #則將其視爲最小開銷節點
            lowest_cost=cost
            lowest_cost_node=node
    return lowest_cost_node

node=find_lowest_cost_node(costs) #在未被處理過的節點中找出最小開銷節點
while node is not None: #循環將在所有節點都被處理過後結束
    cost=costs[node]
    neighbors=graph[node]
    for n in neighbors.keys(): #遍歷當前節點所有鄰居
        newcost=cost+neighbors[n]
        if costs[n]>newcost: #如果經過當前節點前往該鄰居更近
            costs[n]=newcost #則更新起點到該鄰居的距離(所謂的該鄰居的開銷)
            parents[n]=node #同時將該鄰居的父節點設置爲當前節點
    processed.append(node) #當前節點標記爲已處理
    node=find_lowest_cost_node(costs) #找出下一個在costs中的最小開銷節點,進入下一輪循環

輸出最短路徑及最短距離(測試):

def lowest_path(parents):
    record=["fin",]
    child=parents["fin"]
    while child != "start":
        record.append(child)
        child=parents[child]
    record.append("start")
    record.reverse()
    return record

def tatal_weight(record):
    record1=record[1::]
    sum=0
    for i in range(len(record1)):
        sum=sum+graph[record[i]][record1[i]]
    return sum


print(lowest_path(parents))
print(tatal_weight(lowest_path(parents)))

輸出結果

['start', 'A', 'D', 'fin']
8

算法弊端

(1)不能用於有負權值的圖,因爲在該算法中,已處理過的點意味着從起點到該點的開銷已是最小,且不會再對該點進行處理,但負權值可能會拉低這個最小開銷。

(2)不能使用於有環圖。只適用於有向無環圖

(3)有負權值的圖可使用貝爾曼—福德算法,非加權圖可使用廣度優先搜索算法。

 

發佈了17 篇原創文章 · 獲贊 11 · 訪問量 8862
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章