圖由頂點及其邊組成,圖的遍歷主要分爲兩種:
我們只討論第一種情況,即不重複的列出所有的頂點,主要有兩種策略:深度優先搜索(DFS),廣度優先搜索(BFS)
爲了使深度和廣度優先搜索的實現算法的機制更容易理解,假設提供了一個名爲visit的函數,它負責處理每個單獨節點所需的任何處理。 因此,遍歷的目的是按照確定的連接順序在每個節點上調用且僅調用一次該函數。 因爲圖形通常具有前往相同節點的許多不同路徑,所以確保遍歷算法不會多次訪問同一節點我們需要額外的變量來跟蹤已經訪問過哪些節點。 爲此,接下來的兩個部分中的實現定義了一組名爲visited的節點,以跟蹤已經處理的節點。
遍歷圖的深度優先策略類似於樹的前序遍歷,並具有相同的遞歸結構。 唯一的複雜因素是圖表可以包含環。 因此,必須跟蹤已經訪問過的節點。
下面的代碼實現的是從某個特定的節點出發進行的深度優先搜索。
/* *數據結構 node *說明:節點的數據結構、 /* struct Node { string name; //節點名稱 set<Arc *> arcs; //該節點的邊的集合 }; /* *結構:Arc *說明:邊的數據結構 */ struct Arc { Node *start; //即從哪個節點出發 Node *finish; //即指向哪個節點 double cost;//邊的權重 }; * 函數 :depthFirstSearch * 用法: depthFirstSearch(node); * ------------------------------ * 用指定的節點來初始化DFS的起始節點 */ void depthFirstSearch(Node *node) { Set<Node *> visited; visitUsingDFS(node, visited); } /* * 函數: visitUsingDFS * 用法: visitUsingDFS(node, visited); * ------------------------------------ * 從特定的節點執行DFS ,並避免重複遍歷相關節點 */ void visitUsingDFS(Node *node, set<Node *> & visited) { //如果在visited集合中存在該節點,說明爲simple case,直接返回 if (visited.contains(node)) return; //否則 visit(node);//對該節點進行相應操作 visited.add(node);//將該節點添加到visited集合中 foreach (Arc *arc in node->arcs) { //對每一個節點遞歸調用DFS算法 visitUsingDFS(arc->finish, visited); } }
下面用一個實例來體會一下DFS的具體過程。還是上次的那張圖
節點被繪製爲空心圓圈以指示它們尚未被訪問。 隨着算法的進行,這些圓圈中的每一個都將標有記錄處理該節點的順序的數字。
對depthFirstSearch函數本身的調用會創建一個空的set集合,然後將控制權移交給遞歸的visitUsingDFS函數。 假設我們的算法從節點San Francisco處開始,該節點記錄在圖中,如下所示:
foreach (Arc *arc in node->arcs) { visitUsingDFS(arc->finish, visited); }
當然我們可以更爲方便的用棧來存放標記的元素,這樣我們可以在回溯的時候,將元素從棧中彈出即可。當棧空時即表明圖的遍歷完成,棧始終不空,表明圖中有一直不能訪問到的點,因爲沒有路徑通過,此時圖爲不連通的。
那麼同樣的對於下面的有向圖,也可以如下進行搜索,如圖
假設從節點A開始用DFS算法進行遍歷,使用棧來存儲標記過的節點,那麼具體如何實現呢?我會在接下來的博文中詳細分析。