Python 圖_系列之縱橫對比 Bellman-Ford 和 Dijkstra 最短路徑演算法

一枚大果殼發表於2022-04-07

1. 前言

因無向、無加權圖的任意頂點之間的最短路徑由頂點之間的邊數決定,可以直接使用原始定義的廣度優先搜尋演算法查詢。

但是,無論是有向、還是無向,只要是加權圖,最短路徑長度的定義是:起點到終點之間所有路徑中權重總和最小的那條路徑。

如下圖所示,A 到 C 的最短路徑並不是 A 直接到 C(權重是 9),而是 A 到 B 再到 C(權重是 7)。所以,需要在廣度優先搜尋演算法的基礎上進行演算法升級後才能查詢到。

在這裡插入圖片描述

加權圖的常用最短路徑查詢演算法有:

  • 貝爾曼-福特(Bellman-Ford)演算法
  • Dijkstra(迪傑斯特拉) 演算法
  • A* 演算法
  • D* 演算法

2. 貝爾曼-福特(Bellman-Ford)演算法

貝爾曼-福特演算法取自於創始人理查德.貝爾曼萊斯特.福特,本文簡稱 BF 演算法

BF 演算法屬於迭代、窮舉演算法,演算法效率較低,如果圖結構中頂點數量為 n,邊數為 m ,則該演算法的時間複雜度為 m*n ,還是挺大的。

理論上講,圖結構中邊上的權重一般用來描述現實世界中的速度、時間、花費、里程……基本上都是非負數。即使是負數,BF 演算法也能工作得較好。

2.1 BF 演算法思想

問題:如下圖,搜尋 A 到其它任意頂點之間的最短路徑。

首先給每一個頂點一個權重值(用來儲存從起始頂點此頂點的最短路徑上所有邊上權重之和),剛開始除了出發點的權重 0 ,因為還不能確定到其它任意頂點的具體路徑長度,其它頂點的權重值均初始為無窮大(只要是一個適當值都可以)。

下面的圖結構是無向加權圖,對於有向加權圖同樣適用 BF 演算法。

在這裡插入圖片描述

BF 演算法流程:

  • 更新頂點的權重: 計算任一條邊上一端頂點(始點)到另一個端頂點(終點)的權重新權重=頂點(始點)權重+邊的權重,然後使用新權重值更新終點的原來權重值

  • 更新原則: 只有當頂點原來的權重大於新權重時,才更新。

    如:先選擇 A -> B 之間的路徑,因為 A~B 是無向邊,需要計算 2 次。如果是有向圖,則只計算一個方向。

    先計算 A -> B 的新權重=A的權重+(AB)邊上的權重,新權重=0+3=3。因 3 小於 B 頂點現在的權重(無窮大),B 的權重被更新為 3

    再計算 B -> A 的新權重=B的權重+(AB) 邊上的權重。新權重=3+3=66 大於 A 現有的權重 0,則 A 頂點不更新。

    此時,意味著 A -> B 的最短路徑長度為 3

    A B 的前序頂點。當然,這絕對不是最後的結論。

在這裡插入圖片描述

  • 對圖中每一條邊兩端的頂點都執行上述同樣的操作,對於執行順序沒有特定要求。

    如下繼續計算 (A,C) 邊的兩端頂點的權重。

    A -> C 的新權重=0+5=5,更新 C 頂點權重為 5

    C -> A 的新權重=5+5=10 不更新。結論:AC 的前序頂點。

在這裡插入圖片描述

計算 (B,C) 權重:

B -> C 的新權重=3+1=4,小於 C 現有權重 5C 的權重更新為 4則 B 是 C的前序頂點

C -> B 的新權重= 4+1 =5 ,大於 B 現有權重,不更新。

經過上述操作後 (A,B,C)3 個頂點的前序關係:

AB 的前序、BC 的前序,當前 A -> B 的最短路徑是 3A -> B -> C 的最短路徑是 4,但不一定是最後結論。

在這裡插入圖片描述

Tips: 當頂點的權重更新後,也意味著前序頂點也發生了變化。如上述權重更新過程中 C 剛開始前序是 A,後來又更改成了 B

  • 當所有邊兩端頂點都更新完畢後,需要再重新開始,對圖結構中所有邊上的頂點權重再進行一次更新,一至到不再引起權重變化時 BF 演算法才算結束。

  • BF 演算法的本質還是廣度優先搜尋演算法,附加了更新頂點權重的邏輯。

2.2 類結構設計:

本文的圖結構儲存使用連結表。

頂點類: 此類用來描述頂點本身資訊,除了有頂點的常規屬性,如編號、名稱、連結表……外,還需要新增 2 個屬性:

  • 頂點的權重:初始化時為無窮大。

    頂點權重用來儲存起始點到此頂點的最短路徑長度(邊上權重之和)。

  • 前序頂點:BF 演算法中,如果頂點的權重發生了更新,也意味著前序頂點也發生了變化。

初始化方法:

"""
節(頂)點類
"""
import sys
class Vertex:
    def __init__(self, v_name, v_id=0):
        # 頂點的編號
        self.v_id = v_id
        # 頂點的名稱
        self.v_name = v_name
        # 是否被訪問過:False 沒有 True:有
        self.visited = False
        # 與此頂點相連線的其它頂點
        self.connected_to = {}
        # 前序頂點
        self.preorder_vertex = None
        # 權重(初始為無窮大)
        self.weight = sys.maxsize

新增相鄰頂點方法:

    '''
    新增鄰接頂點
    nbr_ver:相鄰頂點
    weight:無向無權重圖,權重預設設定為 1
    '''
    def add_neighbor(self, nbr_ver, weight=1):
        # 字典中以相鄰頂點為鍵,權重為值
        self.connected_to[nbr_ver] = weight
        
    '''
    判斷給定的頂點是不是當前頂點的相鄰頂點
    '''
    def is_neighbor(self, nbr_v):
        return nbr_v in self.connected_to

頂點物件以字串格式輸出:

    '''
    顯示與當前頂點相鄰的其它頂點
    '''  
    def __str__(self):
       	return '權重{0}:名稱{1}頂點相鄰的頂點有:{2}'.format(self.weight, self.v_name,
                                                str([(key.v_name, val) for key, val in self.connected_to.items()]))
                  

頂點權重更新方法:

    '''
    得到和某一個相鄰頂點的權重
    '''
    def get_weight(self, nbr_v):
        return self.connected_to[nbr_v]

    '''
    計算頂點權重(路徑長度)
    '''
    def cal_bf_weight(self, nbr_v):
        # 頂點權重加上頂點間邊的權重
        new_weight = self.weight + self.get_weight(nbr_v)
        if new_weight < nbr_v.weight:
            # 計算出來權重小於頂點原來權重,則更新
            nbr_v.weight = new_weight
            # 設定前序頂點
            nbr_v.preorder_vertex = self
            return 1
        return 0

上述方法為 BF 演算法的關鍵,引數 nbr_v 是指與當前頂點相鄰的頂點。先是計算和當前頂點的新權重,根據更新原則進行更新。如果有更新則需要把當前頂點指定為前序頂點。

Tips: 在圖結構中,最短路徑演算法中的前序頂點指到達此頂點最近的頂點。

圖類: 此類用來對圖中的頂點進行維護,如新增新頂點,維護頂點之間的關係、提供搜尋演算法……

初始化方法:

class Graph:
    def __init__(self):
        # 一維列表,儲存節點
        self.vert_list = {}
        # 頂點個數
        self.v_nums = 0
        # 使用佇列模擬佇列或棧,用於路徑搜尋演算法
        self.queue_stack = []
        # 儲存已經更新過的邊
        self.is_update = []

新增新頂點: 新頂點的編號由內部提供,統一管理,保證編號的一致性。

    def add_vertex(self, vert):
        if vert.v_name in self.vert_list:
            # 已經存在
            return
        # 頂點的編號內部生成
        vert.v_id = self.v_nums
        # 所有頂點儲存在圖所維護的字典中,以頂點名為鍵,頂點物件為值
        self.vert_list[vert.v_name] = vert
        # 數量增一
        self.v_nums += 1

查詢頂點:

    '''
    根據頂點名找到頂點物件
    '''
    def find_vertex(self, v_name):
        if v_name in self.vert_list:
            return self.vert_list.get(v_name)
    '''
    查詢所有頂點
    '''
    def find_vertexes(self):
        return [str(ver) for ver in self.vert_list.values()]

新增頂點與頂點之間的關係: 此方法是一個封裝方法,本質是呼叫頂點自身的新增相鄰頂點方法。這裡用到了遞迴演算法,在 BF 演算法中,一輪更新後可能還需要後續多輪更新才能讓每一個頂點上的權重不再變化。這也是 BF 演算法的缺陷。

    '''
    新增節點與節點之間的關係(邊),
    如果是無權重圖,統一設定為 1 
    '''

    def add_edge(self, from_v, to_v, weight=1):
        # 如果節點不存在
        if from_v not in self.vert_list:
            self.add_vertex(from_v)
        if to_v not in self.vert_list:
            self.add_vertex(to_v)
        from_v.add_neighbor(to_v, weight)

貝爾曼-福特演算法: 圖結構中 BF 演算法的實現。

    '''
    貝爾曼-福特演算法
    '''
    def bf_nearest_path(self, from_v):
        # 記錄邊更新次數
        update_count = 0
        # 裝置起始點的權重為 0
        from_v.weight = 0
        # 起始點壓入佇列
        self.queue_stack.append(from_v)
        # 檢查佇列是否為空
        while len(self.queue_stack) != 0:
            # 從佇列獲取頂點
            tmp_v = self.queue_stack.pop(0)
            # 標記為已經處理
            tmp_v.visited = True
            # 得到與其相鄰頂點
            nbr_vs = tmp_v.connected_to.keys()
            # 更新與其相鄰頂點的權重
            for v_ in nbr_vs:
                # 把相鄰頂點壓入佇列
                self.push_queue(v_)
                # 更新權重,並記錄更新次數
                update_count += tmp_v.cal_bf_weight(v_)
                # 無向邊,要雙向更新
                update_count +=v_.cal_bf_weight(tmp_v)
        # 更新完畢後,如果更新次數為 0 ,則不用再更新。
        if update_count != 0:
            self.is_update = []
            self.bf_nearest_path(from_v)

    '''
     把某一頂點的相鄰頂點壓入佇列
     這裡還是使用廣度優先演算法思路儲存下一個需要搜尋的頂點
     '''
    def push_queue(self, vertex):
        # 獲取 vertex 頂點的相鄰頂點
        for v_ in vertex.connected_to.keys():
            # 檢查此頂點是否壓入過
            if v_.visited:
                continue
            self.queue_stack.append(v_)

測試 BF 演算法:

if __name__ == '__main__':
    # 初始化圖
    graph = Graph()
    # 新增節點
    for v_name in ['A', 'B', 'C', 'D', 'E', 'F']:
        v = Vertex(v_name)
        graph.add_vertex(v)

    # 新增頂點之間關係
    v_to_v = [('A', 'B', 3), ('A', 'C', 5), ('B', 'C', 1), ('B', 'D', 2), ('B', 'E', 4), ('C', 'E', 6), ('D', 'E', 7),
              ('D', 'F', 8),
              ('E', 'F', 3)]
    # 無向圖每 2 個頂點之間互為關係
    for v in v_to_v:
        f_v = graph.find_vertex(v[0])
        t_v = graph.find_vertex(v[1])
        graph.add_edge(f_v, t_v, v[2])
        graph.add_edge(t_v, f_v, v[2])

    # 輸出所有頂點
    print('-----------頂點及頂點之間的關係-------------')
    for v in graph.find_vertexes():
        print(v)

    # 查詢起始點到任一頂點之間的最短路徑長度
    f_v = graph.find_vertex('A')
    graph.bf_nearest_path(f_v)

    print('-----------BF 演算法後頂點及頂點之間的關係-------------')
    for v in graph.find_vertexes():
        print(v)

    # 查詢從起始點到任意頂點間的最短路徑長度
    print('----------f_v~t_v 最短路徑長度------------')
    for name in ['B', 'C', 'D', 'E', 'F']:
        t_v = graph.find_vertex(name)
        path = [t_v]

        while True:
            v = t_v.preorder_vertex
            path.insert(0, v)
            if v.v_id == f_v.v_id:
                break
            t_v = v
        print([(v.v_name, v.weight) for v in path])

測試結果:

-----------頂點及頂點之間的關係-------------
權重9223372036854775807:名稱A頂點相鄰的頂點有:[('B', 3), ('C', 5)]
權重9223372036854775807:名稱B頂點相鄰的頂點有:[('A', 3), ('C', 1), ('D', 2), ('E', 4)]
權重9223372036854775807:名稱C頂點相鄰的頂點有:[('A', 5), ('B', 1), ('E', 6)]
權重9223372036854775807:名稱D頂點相鄰的頂點有:[('B', 2), ('E', 7), ('F', 8)]
權重9223372036854775807:名稱E頂點相鄰的頂點有:[('B', 4), ('C', 6), ('D', 7), ('F', 3)]
權重9223372036854775807:名稱F頂點相鄰的頂點有:[('D', 8), ('E', 3)]
-----------BF 演算法後頂點及頂點之間的關係-------------
權重0:名稱A頂點相鄰的頂點有:[('B', 3), ('C', 5)]
權重3:名稱B頂點相鄰的頂點有:[('A', 3), ('C', 1), ('D', 2), ('E', 4)]
權重4:名稱C頂點相鄰的頂點有:[('A', 5), ('B', 1), ('E', 6)]
權重5:名稱D頂點相鄰的頂點有:[('B', 2), ('E', 7), ('F', 8)]
權重7:名稱E頂點相鄰的頂點有:[('B', 4), ('C', 6), ('D', 7), ('F', 3)]
權重10:名稱F頂點相鄰的頂點有:[('D', 8), ('E', 3)]
----------起始點到任意頂點之間的最短路徑長度------------
# A->B 最短路徑長度 3
[('A', 0), ('B', 3)]   
# A->B->C 最短路徑長度為 4
[('A', 0), ('B', 3), ('C', 4)]
# A->B->D 最短路徑長度為 5
[('A', 0), ('B', 3), ('D', 5)]
# A->B->E 最短路徑長度為 7
[('A', 0), ('B', 3), ('E', 7)]
# A->B->E->F 最短路徑長度為 10
[('A', 0), ('B', 3), ('E', 7), ('F', 10)]

在這裡插入圖片描述

3. Dijkstra(迪傑斯特拉)演算法

迪傑斯特拉演算法(Diikstra) 是由荷蘭電腦科學家狄克斯特拉於1959 年提出的,因此又叫狄克斯特拉演算法。為了便於表述,本文簡稱 DJ 演算法。

DJ 演算法和前面所聊的 BF 演算法,可謂同工異曲,演算法的核心思想是相同的:

  • 搜尋到某一個頂點後,更新與其相鄰頂點的權重

    權重計演算法則以及權重更新原則兩者相同。

  • 且頂點權重的資料含義和 BF 演算法的一樣。表示從起始點到此點的最短路徑長度(也就是經過的所有邊的權重之和)。

    初始時,因還不能具體最短路徑,起始點的權重為 0 ,其它頂點權重可設定為無窮大。

DJ 演算法相比較 BF 演算法有 2 個不同的地方:

  • 在無向加權圖中,BF 演算法需要對相鄰 2 個頂點進行雙向權重計算。

  • DJ 演算法搜尋時,每次選擇的下一個頂點是所有權重值最小的頂點。其思想是保證每一次選擇的頂點和當前頂點權重都是最短的。

    如下圖結構中,查詢 A 到任一頂點的最短路徑:

在這裡插入圖片描述

  1. 定位到起始點 AA 頂點也稱為當前頂點

設定 A 的權重為 0,A 的相鄰頂點有 BC,需要計算 AB 以及 AC 之間的權重。這裡是先選擇 B 還是 C 並不重要。

先選擇 B 頂點,計算 A -> B 的路徑長度權重。新權重計算公式=A頂點權重+(A,B)邊的權重=0+3=3

更新原則和 BF 演算法一樣,當計算出來的權重值小於相鄰頂點的權重值時,更新。於是 B 的權重更新為 .此時 AB 的前序頂點。

再選擇 C 頂點,計算 A -> C 路徑長度權重=0+9=9,因 9 小於 C 現在的無窮大權重,C 的權重被更新為 9.

到這裡, 可以說 DJ 演算法和 BF 演算法沒有什麼不一樣。

但是,DJ 演算法不需要對邊上 2 個頂點進行雙向權重計算,這是 DJ 演算法與 BF 演算法的第一個差異性。

此時,更新後的圖結構如下所示:

在這裡插入圖片描述

很顯然, BC 將成為下一個搜尋的候選點,這裡 DJ 演算法和 BF 演算法就有了很大的差異性。

BF 演算法對於選擇 B 還是 C 的順序沒有要求。

DJ 演算法則不同,會選擇 BC 中權重值更小的那個, B 的權重 3 小於 C 的權重 9 ,當然選擇 B 為下一個搜尋頂點。

這是 BF 演算法和 DJ 演算法的第二個差異性!

選擇 B 後也就意味著 A->B 之間的最短路徑就確定了。為什麼?

因你無法再找出一條比之更短的路徑。

這裡也是 DJ 演算法的關鍵思想,在選擇一個權重最小的候選頂點後,就能確定此頂點和它的前序頂點之間的最短路徑長度。

到現在為止, B 的前序頂點是 A;C 的前序頂點也是 A 。

因為 B 已經被選為下一個搜尋頂點,於是 B 頂點和它的前序頂點 A 之間的最短路徑已經出來了。

A->B 最短路徑長度為 3。

而 C 頂點還沒有成為搜尋頂點,其和 A 頂點之間的最短路徑還是一個未知數。

  1. **B成為當前頂點 **

找到與 B 相鄰的 CDE 3個頂點,然後分別計算路徑長度權重。

B->C 的新權重=3+4=7 小於 C 現有的權重 9 ,C 的權重被更新為 7 ,C 的前序頂點也變成了 B

同理,B->D 路徑中 D 的權重被更新為 5;B->E 路徑中 E 的權重被更新為 6 。

在這裡插入圖片描述

再在 CDE 3 個頂點中選擇權重值最小的 D 為下一個搜尋頂點.到這裡!可以發現 DJ 演算法和原始定義的廣度搜尋演算法以及 BF 之間就有了本質上的區別:

  • 廣度優先搜尋演算法會選擇和 B 同時進入佇列的 C 頂點成為下一個搜尋頂點。因為 B 和 C 都是離 A 較近的頂點。
  • DJ 演算法是在候選頂點中,哪一個頂點的權重最少,就選擇誰,不採用就近原則.而是以頂點的權重值小作為優先選擇條件.

選擇 D 後 ,各頂點間的關係:

B 的前序是 A,(A,B)間最短路徑已經確定。

D 的前序是 B ,(B,D)間的最短路徑可以確定,又因為 B 的前序頂點是 A ,所以 A->B->D 的最短路徑可以確定。

其它項點間的最短路徑暫時還不明朗。

  1. D 頂點為當前頂點

計算與 D 相鄰的 E、F 頂點的權重。

D->E 的新權重=5+7=12 大於 E 現有權重 6 ,不更新。E 的前序頂點還是 B

D->F 的新權重=5+8=13 ,F 的權重由無窮大更新為 13

在這裡插入圖片描述

再在省下的所有可選頂點中選擇權重值小的 E 頂點為下一個搜尋點,當選擇 E 後:

E 的前序為 B , B 的前序是 A,所以 A 到 E 的最短路徑長度就是 A->B->C ,路徑長度為 6。

  1. E 為當前頂點,計算和其相鄰頂點間的權重。

唯一可改變的是 F 頂點的權重,F 頂點的前序頂點變為 E

在這裡插入圖片描述

  1. 再選擇 C 為當前頂點

C 和相鄰頂點不會再產生任何權重的變化,其前序頂點還是 B

所以 AC 之間的最短路徑應該是 A->B->C 路徑長度為 7。

在這裡插入圖片描述

最後選擇 F 頂點,也不會再產生任何權重變化,F 的前序是 EE的前序是BB的前序是A,所 AF 的最短路徑應該是 A->B->E->F 權重值為 9。

最後,以圖示方式,比較 BF 演算法和 DJ 演算法中各頂點出佇列的順序:

BF 採用就近原則出佇列,然後不停計算相鄰頂點的權重,至到權重不再變化為止,顯然用的是蠻力。

DJ 採用權重值小的優先出佇列,顯然用的是巧力。

在這裡插入圖片描述

分析完 DJ 演算法的流程,現在編寫程式碼:

和上面的 BF 演算法相比較,頂點類一樣,在圖類中新增 DJ 演算法:

DJ 演算法的本質還是廣度優先搜尋演算法,有所區別的地方是使用優先佇列,每次從佇列中選擇頂點時,選擇頂點權重最小的。

所以在圖類中,需要修改或過載一個候選頂點入佇列的方法:

    '''
     把當前頂點的相鄰頂點壓入佇列
     '''
    def push_queue_dj(self, vertex):
        # 獲取 vertex 頂點的相鄰頂點
        for v_ in vertex.connected_to.keys():
            # 檢查此頂點是否壓入過
            if v_.visited:
                continue
            # vertex.visited = True
            self.queue_stack.append(v_)
        # 對佇列按頂點權重排序,保證權重值小的排在前面。是 DJ 演算法的關鍵
        self.queue_stack.sort(key=lambda v: v.weight)
        
    '''
    檢查邊是否已經更新過
    DJ 演算法中已經更新過的邊不需要再更新
    '''
    def is_updated(self, *edge):
        s = ord(edge[0].v_name) + ord(edge[1].v_name)
        for e in self.is_update:
            s1 = ord(e[0].v_name) + ord(e[1].v_name)
            if s == s1:
                return True
        return False

實現 DJ 演算法:

	'''
    Dijkstra(迪傑斯特拉)演算法 
    '''
    def dj_nearest_path(self, from_v):
        # 裝置起始點的權重為 0
        from_v.weight = 0
        # 起始點壓入佇列
        self.queue_stack.append(from_v)
        # 檢查佇列是否為空
        while len(self.queue_stack) != 0:
            # 從佇列獲取頂點
            tmp_v = self.queue_stack.pop(0)
            # 標記為已經處理
            tmp_v.visited = True
            # 得到與其相鄰所有頂點
            nbr_vs = tmp_v.connected_to.keys()
            # 更新與其相鄰頂點的權重
            for v_ in nbr_vs:
                # 邊是否已經處理
                if self.is_updated(tmp_v, v_):
                    continue
                # 更新權重
                tmp_v.cal_bf_weight(v_)
                # 記錄已經更新過
                self.is_update.append((tmp_v, v_))
                # 把相鄰頂點壓入佇列
                self.push_queue_dj(v_)

測試程式碼:

if __name__ == '__main__':
    # 初始化圖
    graph = Graph()
    # 新增節點
    for v_name in ['A', 'B', 'C', 'D', 'E', 'F']:
        v = Vertex(v_name)
        graph.add_vertex(v)

    # 新增頂點之間關係
    v_to_v = [('A', 'B', 3), ('A', 'C', 9), ('B', 'C', 4), ('B', 'D', 2), ('B', 'E', 3), ('C', 'E', 6), ('D', 'E', 7),
              ('D', 'F', 8),
              ('E', 'F', 3)]
    # 無向圖每 2 個頂點之間互為關係
    for v in v_to_v:
        f_v = graph.find_vertex(v[0])
        t_v = graph.find_vertex(v[1])
        graph.add_edge(f_v, t_v, v[2])
        graph.add_edge(t_v, f_v, v[2])

    # 輸出所有頂點
    print('-----------頂點及頂點之間的關係-------------')
    for v in graph.find_vertexes():
        print(v)

    # 查詢起始點到任一頂點之間的最短路徑長度
    f_v = graph.find_vertex('A')
    # DJ 演算法
    graph.dj_nearest_path(f_v)

    print('----------- DJ 演算法後頂點及頂點之間的關係-------------')
    for v in graph.find_vertexes():
        print(v)

    #
    print('----------f_v~t_v 最短路徑長度------------')
    for name in ['B', 'C', 'D', 'E', 'F']:
        t_v = graph.find_vertex(name)
        path = [t_v]

        while True:
            # 找到前序頂點
            v = t_v.preorder_vertex
            path.insert(0, v)
            if v.v_id == f_v.v_id:
                break
            t_v = v
        print([(v.v_name, v.weight) for v in path])

輸出結果:

-----------頂點及頂點之間的關係-------------
權重9223372036854775807:名稱A頂點相鄰的頂點有:[('B', 3), ('C', 9)]
權重9223372036854775807:名稱B頂點相鄰的頂點有:[('A', 3), ('C', 4), ('D', 2), ('E', 3)]
權重9223372036854775807:名稱C頂點相鄰的頂點有:[('A', 9), ('B', 4), ('E', 6)]
權重9223372036854775807:名稱D頂點相鄰的頂點有:[('B', 2), ('E', 7), ('F', 8)]
權重9223372036854775807:名稱E頂點相鄰的頂點有:[('B', 3), ('C', 6), ('D', 7), ('F', 3)]
權重9223372036854775807:名稱F頂點相鄰的頂點有:[('D', 8), ('E', 3)]
----------- DJ 演算法後頂點及頂點之間的關係-------------
權重0:名稱A頂點相鄰的頂點有:[('B', 3), ('C', 9)]
權重3:名稱B頂點相鄰的頂點有:[('A', 3), ('C', 4), ('D', 2), ('E', 3)]
權重7:名稱C頂點相鄰的頂點有:[('A', 9), ('B', 4), ('E', 6)]
權重5:名稱D頂點相鄰的頂點有:[('B', 2), ('E', 7), ('F', 8)]
權重6:名稱E頂點相鄰的頂點有:[('B', 3), ('C', 6), ('D', 7), ('F', 3)]
權重9:名稱F頂點相鄰的頂點有:[('D', 8), ('E', 3)]
----------f_v~t_v 最短路徑長度------------
[('A', 0), ('B', 3)]
[('A', 0), ('B', 3), ('C', 7)]
[('A', 0), ('B', 3), ('D', 5)]
[('A', 0), ('B', 3), ('E', 6)]
[('A', 0), ('B', 3), ('E', 6), ('F', 9)]

DJ 演算法不適合用於邊上權重為負數的圖中,否則可能找不到路徑。

3. 總結

在加權圖中查詢最短路徑長度演算法除了 BF、DJ 演算法,還有 A* 演算法 D* 演算法。有興趣的可以自行了解。

相關文章