【小算法】圖的遍歷之廣度優先(BFS)

談到算法,圖的操作是避免不了。

而我們一般談到圖時,又必定會談到圖的遍歷。

圖的遍歷通常有 2 種,深度優先(DFS) 和廣度優先(BFS)。

深度優先可以閱讀我這篇博文:【小算法】圖的遍歷之深度優先(DFS)

本篇博文講解廣度優先(BFS)。

圖的表示

圖有兩種表示方式

在這裏插入圖片描述

1. 臨接矩陣

在這裏插入圖片描述

其實就是一個權重矩陣,用 1 代表兩個結點有連接,0 表示沒有連接,這樣的表示方式通俗易懂,特別適合稠密圖,也就是大多數結點是亮亮連接的情況。

2. 臨接表

在這裏插入圖片描述

用一個數組儲存所有的頂點的信息,每個頂點又用一個鏈表或者是數組存放與它相臨的結點的信息。

這樣的表示方式特別適合稀疏圖,也就是比較少的結點之間相互有連接。

本文示例代碼用 Python 表示,爲了簡便,用臨接表這種形式表示

BFS 算法思路

其實 BFS 的思路非常簡單。

如果你哪天錢包忘記在哪裏了,以 BFS 的思路就是有層次地搜索。

先每個房間快速地瞄一眼,如果沒有發現的話,那麼就在每個房間的牀上、桌子上快速瞄一眼。

如果還是不行的話,再在每個傢俱的每個櫃子裏快速瞄一眼。

然後,按照這樣一層一層進行下去。

DFS 圖例

在這裏插入圖片描述

上面是一張圖,如果要遍歷圖中所有的結點,又不重複。

在實際編碼中,如果要用 BFS 的方式去遍歷一個圖的話,通常我們會用一個隊列來動態保存陸續訪問的結點。

我們首先選擇 A.

所以 A 先入隊列。

在這裏插入圖片描述

A 有 2 個臨接結點 B 和 C,所以 B 和 C 依次入隊列。

並且將 A 從隊列中彈出。

在這裏插入圖片描述

A 結點出隊後,現在隊列首個元素是 B 結點,B 結點有 4 個臨接點 A、C、D、F。

因爲 A 和 C 已經入隊列過一次,所以不能再入,因此將 D、F 入列。

最後,將 B 結點及時彈出來。

在這裏插入圖片描述

B 結點彈出來後,C 結點就是隊列的首元素,它有 A、B、E 3 個臨結點,但是 A 和 B 已經入隊列訪問過,所以它只需要將 E 入列就好了。

然後,C 將自己彈出來。

隊列首元素就變成了 D.

在這裏插入圖片描述

由於 D 結點所有的臨接點都已經入隊列過了,代表訪問過了,所以它彈出自己就好。

後面的 F 和 E 也是這樣的邏輯,就不贅述了。

最後,隊列所有的元素彈出後,也沒有新的元素可以再入列的時候,也就說明完整的 BFS 過程結束了。

巧妙地利用隊列先進先出(FIFO)的特性用來保存遍歷的結點痕跡,最終實現了無重複也無遺漏的圖的遍歷,這種算法思想非常的贊。

下面用代碼示例驗證一下,python 版本是 3.6

Python 代碼

bfs.py

import queue

# G 是臨接表的方式表達圖形
G={}
G[0] = {1,2}
G[1] = {2,3,5}
G[2] = {0,1,3}
G[3] = {2,4,5}
G[4] = {1,3,5}
G[5] = {1,3,4}

# 記錄訪問過的結點
visited = [False,False,False,False,False,False]
# 結點的別名
names=['A','B','C','D','E','F']

vertex_queue = queue.Queue()


def bfs(root):

    vertex_queue.put(root)
    visited[root] = True

    while not vertex_queue.empty():

        v = vertex_queue.get()
    
        print("----->",names[v])

        for i,value in enumerate(G[v]):
            # 已經入過 queue 的結點就不用再入
            if not visited[value]:
                visited[value] = True
                vertex_queue.put(value)
        


if __name__ == "__main__":
    # 打印圖
    print("Graphy:",G)
    #dfs(0)
    bfs(0)

最終結果如下:

Graphy: {0: {1, 2}, 1: {2, 3, 5}, 2: {0, 1, 3}, 3: {2, 4, 5}, 4: {1, 3, 5}, 5: {1, 3, 4}}
---> A
---> B
---> C
---> D
---> F
---> E

可以看到,代碼可以正常執行,大家親手實踐一下吧。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章