圖的遍歷之深度優先搜索(DFS)


深度優先搜索depth-first search)是對先序遍歷preorder traversal)的推廣。”深度優先搜索“,顧名思義就是盡可能的搜索一個圖。想象你是身處一個迷宮的入口,迷宮中的路每一個拐點有一盞燈是亮着的,你的任務是將所有燈熄滅,按照DFS的做法如下:

1. 熄滅你當前所在的拐點的燈

2. 任選一條路向前(深處)走,每經過一個拐點將燈熄滅直到與之相鄰的拐點的燈全部熄滅后,原路返回到某個拐點的相鄰拐點燈是亮着的,走到燈亮的拐點,重復執行步驟1

3. 當所有燈熄滅時,結束

將上面的例子抽象出來的DFS的算法描述(C偽代碼)如下:

//布爾型數組Visited[]初始化成false
void DFS(Vetex v) { Visited[v] = true; for each w adjacent to v if (!Visited[w]) DFS(w); }

 可以看出上述的DFS為遞歸算法,可以利用將其轉為非遞歸。C偽代碼如下:

//布爾型數組Visited[]初始化成false
void DFS(Vertex v) { Visited[v] = true; 
Stack sta
= MakeStack(MAX_SIZE); Push(sta, v); while (!Empty(sta)) { Vertex w = Pop(sta); for each u adjacent to w { if (!Visited[u]) { Push(sta, u); Visited[u] = true;
} } } }

引理: 若圖G是連通的,則通過深度優先搜索可以對它的所有頂點進行標記,並且在算法的執行過程中,它的每一條邊至少被查看過一次。

證明: 假設結論不成立,令U表示算法最終未被標記過的頂點的集合。由於G是連通的,因此在U中至少有一個頂點與一個被標記過的頂點相連。但是這種情況不可能成立,因為一旦一個頂點被訪問過了,則所有與它相連的未被標記過的頂點也會被訪問(從而也被標記)。故所有頂點都會被訪問而標記,並且一旦某個頂點被訪問,它相連的邊就會被查看,所以每條邊都將被查看過。

然而,如果一個圖G不是連通的,要標記所有頂點,需對DFS稍作修改:若在第一次嘗試所有頂點都被標記過,則圖是連通的,否則,從任意一個未被標記的頂點開始,再次執行DFS。所以我們可以利用DFS確定一個圖是否連通。C偽代碼描述上述算法如下:

/*返回連通成分的數目*/
int ConnectedComponents ( Graph G ) { int componentNum = 0; for ( each v in G ) if ( !visited[V] ) { DFS( v ); componentNum += 1; } return componentNum; } 

 上述算法的復雜度:

若有N個頂點、 E條邊,時間復雜度是
  用鄰接表存儲圖,有O(N+E)
  用鄰接矩陣存儲圖,有O(N^2)

 深度優先搜索的相關練習:

  poj-1979 Red and Black

  poj-2386 Lake Counting

  列出連通集

  06-圖2 Saving James Bond - Easy Version

      poj-2488 A Knight's Journey

  拓展閱讀:

        深度優先生成樹及其應用

參考資料:

《數據結構與算法分析-C語言描述》

《算法引論》

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM