⑴ 圖遍歷演算法之DFS/BFS
在計算機科學, 圖遍歷(Tree Traversal,也稱圖搜索)是一系列圖搜索的演算法, 是單次訪問樹結構類型數據(tree data structure)中每個節點以便檢查或更新的一系列機制。圖遍歷演算法可以按照節點訪問順序進行分類,根據訪問目的或使用場景的不同,演算法大致可分為28種:
圖遍歷即以特定方式訪問圖中所有節點,給定節點下有多種可能的搜索路徑。假定以順序方式進行(非並行),還未訪問的節點就需通過堆棧(LIFO)或隊列(FIFO)規則來確定訪問先後。由於樹結構是一種遞歸的數據結構,在清晰的定義下,未訪問節點可存儲在調用堆棧中。本文介紹了圖遍歷領域最流行的廣度優先搜索演算法BFS和深度優先搜索演算法DFS,對其原理、應用及實現進行了闡述。通常意義上而言,深度優先搜索(DFS)通過遞歸調用堆棧比較容易實現,廣義優先搜索通過隊列實現。
深度優先搜索(DFS)是用於遍歷或搜索圖數據結構的演算法,該演算法從根節點開始(圖搜索時可選擇任意節點作為根節點)沿著每個分支進行搜索,分支搜索結束後在進行回溯。在進入下一節點之前,樹的搜索盡可能的加深。
DFS的搜索演算法如下(以二叉樹為例):假定根節點(圖的任意節點可作為根節點)標記為 ,
(L) : 遞歸遍歷左子樹,並在節點 結束。
(R): 遞歸遍歷右子樹,並在節點 結束。
(N): 訪問節點 。
這些步驟可以以任意次序排列。如果(L)在(R)之前,則該過程稱為從左到右的遍歷;反之,則稱為從右到左的遍歷。根據訪問次序的不同,深度優先搜索可分為 pre-order、in-order、out-order以及post-order遍歷方式。
(a)檢查當前節點是否為空;
(b)展示根節點或當前節點數據;
(c)遞歸調用pre-order函數遍歷左子樹;
(d)遞歸調用pre-order函數遍歷右子樹。
pre-order遍歷屬於拓撲排序後的遍歷,父節點總是在任何子節點之前被訪問。該遍歷方式的圖示如下:
遍歷次序依次為:F -B -A-D- C-E-G- I-H.
(a)檢查當前節點是否為空;
(b)遞歸調用in-order函數遍歷左子樹;
(c)展示根節點或當前節點數據;
(d)遞歸調用in-order函數遍歷右子樹。
在二叉樹搜索中,in-order遍歷以排序順序訪問節點數據。該遍歷方式的圖示如下:
遍歷次序依次為:A -B - C - D - E - F - G -H-I
(a)檢查當前節點是否為空;
(b)遞歸調用out-order函數遍歷右子樹;
(c)展示根節點或當前節點數據;
(d)遞歸調用out-order函數遍歷左子樹。
該遍歷方式與LNR類似,但先遍歷右子樹後遍歷左子樹。仍然以圖2為例,遍歷次序依次為:H- I-G- F- B- E- D- C- A.
(a)檢查當前節點是否為空;
(b)遞歸調用post-order函數遍歷左子樹;
(c)遞歸調用post-order函數遍歷右子樹;
(d)展示根節點或當前節點數據。
post-order遍歷圖示如下:
遍歷次序依次為:A-C-E-D-B-H-I-G-F.
pre-order遍歷方式使用場景:用於創建樹或圖的副本;
in-order遍歷使用場景:二叉樹遍歷;
post-order遍歷使用場景:刪除樹
遍歷追蹤也稱樹的序列化,是所訪問根節點列表。無論是pre-order,in-order或是post-order都無法完整的描述樹特性。給定含有不同元素的樹結構,pre-order或post-order與in-order遍歷方式結合起來使用才可以描述樹的獨特性。
樹或圖形的訪問也可以按照節點所處的級別進行遍歷。在每次訪問下一層級節點之前,遍歷所在高層級的所有節點。BFS從根節點(圖的任意節點可作為根節點)出發,在移動到下一節點之前訪問所有相同深度水平的相鄰節點。
BFS的遍歷方法圖示如下:
遍歷次序依次為: F-B-G-A-D-I-C-E-H.
圖演算法相關的R包為igraph,主要包括圖的生成、圖計算等一系列演算法的實現。
使用方法:
參數說明:
示例:
結果展示:
DFS R輸出節點排序:
使用方法:
參數含義同dfs
示例:
結果展示:
BFS R輸出節點排序:
以尋找兩點之間的路徑為例,分別展示BFS及DFS的實現。圖示例如下:
示例:
輸出結果:
示例:
輸出結果:
[1] 維基網路: https://en.wikipedia.org/wiki/Tree_traversal
[2] GeeksforGeeks: https://www.geeksforgeeks.org/tree-traversals-inorder-preorder-and-postorder/
[3] http://webdocs.cs.ualberta.ca/~holte/T26/tree-traversal.html
[4]Martin Broadhurst, Graph Algorithm: http://www.martinbroadhurst.com/Graph-algorithms.html#section_1_1
[5]igraph: https://igraph.org/r/doc/dfs.html
[6]igraph: https://igraph.org/r/doc/bfs.html
[7] Depth-First Search and Breadth-First Search in python: https://eddmann.com/posts/depth-first-search-and-breadth-first-search-in-python/
⑵ 基本演算法——深度優先搜索(DFS)和廣度優先搜索(BFS)
深度優先搜索和廣度優先搜索,都是圖形搜索演算法,它兩相似,又卻不同,在應用上也被用到不同的地方。這里拿一起討論,方便比較。
一、深度優先搜索
深度優先搜索屬於圖演算法的一種,是一個針對圖和樹的遍歷演算法,英文縮寫為DFS即Depth First Search。深度優先搜索是圖論中的經典演算法,利用深度優先搜索演算法可以產生目標圖的相應拓撲排序表,利用拓撲排序表可以方便的解決很多相關的圖論問題,如最大路徑問題等等。一般用堆數據結構來輔助實現DFS演算法。其過程簡要來說是對每一個可能的分支路徑深入到不能再深入為止,而且每個節點只能訪問一次。
基本步奏
(1)對於下面的樹而言,DFS方法首先從根節點1開始,其搜索節點順序是1,2,3,4,5,6,7,8(假定左分枝和右分枝中優先選擇左分枝)。
(2)從stack中訪問棧頂的點;
(3)找出與此點鄰接的且尚未遍歷的點,進行標記,然後放入stack中,依次進行;
(4)如果此點沒有尚未遍歷的鄰接點,則將此點從stack中彈出,再按照(3)依次進行;
(5)直到遍歷完整個樹,stack里的元素都將彈出,最後棧為空,DFS遍歷完成。
二、廣度優先搜索
廣度優先搜索(也稱寬度優先搜索,縮寫BFS,以下採用廣度來描述)是連通圖的一種遍歷演算法這一演算法也是很多重要的圖的演算法的原型。Dijkstra單源最短路徑演算法和Prim最小生成樹演算法都採用了和寬度優先搜索類似的思想。其別名又叫BFS,屬於一種盲目搜尋法,目的是系統地展開並檢查圖中的所有節點,以找尋結果。換句話說,它並不考慮結果的可能位置,徹底地搜索整張圖,直到找到結果為止。基本過程,BFS是從根節點開始,沿著樹(圖)的寬度遍歷樹(圖)的節點。如果所有節點均被訪問,則演算法中止。一般用隊列數據結構來輔助實現BFS演算法。
基本步奏
(1)給出一連通圖,如圖,初始化全是白色(未訪問);
(2)搜索起點V1(灰色);
(3)已搜索V1(黑色),即將搜索V2,V3,V4(標灰);
(4)對V2,V3,V4重復以上操作;
(5)直到終點V7被染灰,終止;
(6)最短路徑為V1,V4,V7.