【小算法】圖的遍歷之深度優先(DFS)

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

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

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

本篇博文講解深度優先(DFS)。

圖的表示

圖有兩種表示方式

在這裏插入圖片描述

1. 臨接矩陣

在這裏插入圖片描述

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

2. 臨接表

在這裏插入圖片描述

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

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

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

DFS 算法思路

其實 DFS 的思路非常簡單。

如果你哪天錢包忘記在哪裏了,以 DFS 的思路就是,一個房間一個房間找。

先選定一個房間,大致掃一眼,發現沒有。

然後,就選擇房間裏面的辦公桌,桌子上沒有的話,再去桌子上的櫃子裏面找。

如果桌子沒有,就去牀上着。

如果翻遍了所有的角落,也沒有那就換個房間。

DFS 圖例

在這裏插入圖片描述

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

可以先選擇一個頂點作爲根結點,然後沿着路徑一條一條遍歷下去。

關鍵詞是遞歸,因爲遞歸需要終止條件,所以另外需要用一個數組記錄已經訪問過的結點,當一個結點它的臨結點都已經訪問時,它就需要回溯,這樣能避免重複及陷入死循環。

我們首先選擇 A.

在這裏插入圖片描述

A 有 2 個臨接結點 B 和 C,我們選擇先從 B 出發,所以此時路徑是

A--->B

在這裏插入圖片描述

現在在 B 結點位置,B 有 3 個臨接結點,C、D、F,按照優先往右邊走的原則,我們選擇 F,所以此時的路徑是

A--->B--->F

在這裏插入圖片描述

F 也有 3 個臨接點B,D,E,按照從最右邊的順序遍歷,我們選擇 E,現在路徑是:

A--->B--->F--->E

在這裏插入圖片描述

同樣的邏輯,在 E 點會選擇右邊的 C,這時候路徑是

A--->B--->F--->E--->C

在這裏插入圖片描述

到達 C 點時,情況有些不同,它的臨接點 A 和 B 都已經訪問過了,代表這條路徑到頭了,需要向上回溯。

回溯的過程也是順着路徑往回撤,路徑是

A--->B--->F--->E--->C--->E

在這裏插入圖片描述

E 有 2 個臨接點,因爲 C 點已經訪問了,所以 E 可以訪問 D.

在這裏插入圖片描述

到了 D 點,D 的臨接點都已經訪問過,所以 D 也需要往回溯,這種回溯是遞歸的,最終會回溯到節點 A.

A 是從 B 出發的,按照算法邏輯,這個時候應該從 C 出發了,但是 C 已經被訪問了,所以最終整個遍歷就結束了。

Python 代碼

dfs.py

# 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']

def dfs(v):
    # 打印訪問的點
    print("--->",names[v])
    visited[v] = True
    for i,value in enumerate(G[v]):
        # 如果可以訪問,最沿着路徑一直訪問
        if not visited[value]:
            dfs(value) 



if __name__ == "__main__":
    # 打印圖
    print("Graphy:",G)
    dfs(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
---> E
---> F

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

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