DFS 深度優先搜尋

2021-09-10 08:11:57 字數 4160 閱讀 2727

dfs——深度優先搜尋

dfs(depth-first-search)深度優先搜尋演算法,是搜尋演算法的一種。是一種在開發爬蟲早期使用較多的方法。它的目的是要達到被搜尋結構的葉結點 。目錄1

舉例說明

2特點 3

效率 4

演算法詳解 5

搜尋的過程

下圖是乙個無向圖,如果我們從a點發起深度優先搜尋(以下的訪問次序並不是唯一的,第二個點既可以是b也可以是c,d),則我們可能得到如下的乙個訪問過程:a->b->e(沒有路了!回溯到b)->c->f->h->g->d(沒有路,最終回溯到 a,a也沒有未訪問的相鄰節點,本次搜尋結束).

ab c d

e f g

h每次深度優先搜尋的結果必然是圖的乙個連通分量。深度優先搜尋可以從多點發起。如果將每個節點在深度優先搜尋過程中的"結束時間"排序(具體做法是建立乙個list,然後在每個節點的相鄰節點都已被訪問的情況下,將該節點加入list結尾,然後逆轉整個鍊錶),則我們可以得到所謂的"拓撲排序",即topological sort.

當然,當人們剛剛掌握深度優先搜尋的時候常常用它來走迷宮。事實上我們還有別的方法,那就是廣度優先搜尋 (bfs)。狀態(state):狀態是指問題求解過程中每一步的狀況。

算符(operator)算符是把問題從一種狀態變換到另一種狀態的方法代號。算符的取值範圍就是搜尋的範圍。(一般設為區域性變數)。

節點(node):用來表明狀態特徵及相關資訊。

作為搜尋演算法的一種,dfs對於尋找乙個解的np(包括npc)問題作用很大。但是,搜尋演算法畢竟是時間複雜度是o(n!)的階乘級演算法,它的效率非常低,在資料規模變大時,這種演算法就顯得力不從心了。

關於深度優先搜尋的效率問題,有多種解決方法。最具有通用性的是剪枝(prunning),也就是去除沒有用的搜尋分支。有可行性剪枝和最優性剪枝兩種。此外,對於很多問題,可以把搜尋與動態規劃(dp,dynamic programming)、完備匹配(匈牙利演算法)等高效演算法結合。

首先選定圖的類別(有向圖、無向圖),再選定圖的儲存結構,根據輸入的頂點或者邊建立圖;並把相應的鄰接表或者鄰接矩陣輸出;

根據已有的鄰接矩陣或鄰接錶用遞迴方法編寫深度優先搜尋遍歷演算法,並輸出遍歷結果;

圖的深度遍歷原則:

1 如果有可能,訪問乙個領接的未訪問的節點,標記它,並把它放入棧中。

2 當不能執行規則 1 時,如果棧不為空,則從棧中彈出乙個元素。

3 如果不能執行規則 1 和規則 2 時,則完成了遍歷。

**中的圖使用的是graph 圖-鄰接矩陣法 來表示,其他的表示法請見:graph 圖-鄰接表法

**中的stack為輔助結構,用來記載訪問過的節點。棧的詳細描述可以見:arraystack 棧 ,linkedstack 棧 。

vertex表示圖中的節點,其中包含訪問,是否訪問,清除訪問標誌的方法。 graph.main:提供簡單測試。**可以以指定下標的節點開始作深度遍歷。 **比較簡單,除了graph.dsf(int i)深度優先遍歷演算法外沒有過多注釋。

當節點v的所有邊都己被探尋過,搜尋將回溯到發現節點v的那條邊的起始節點。這一過程一直進行到已發現從源節點可達的所有節點為止。如果還存在未被發現的節點,則選擇其中乙個作為源節點並重複以上過程,整個程序反覆進行直到所有節點都被訪問為止。屬於盲目搜尋。

事實上,深度優先搜尋屬於圖演算法的一種,英文縮寫為dfs即depth first search.其過程簡要來說是對每乙個可能的分支路徑深入到不能再深入為止,而且每個節點只能訪問一次.

深度優先搜尋是圖論中的經典演算法,利用深度優先搜尋演算法可以產生目標圖的相應拓撲排序表,利用拓撲排序表可以方便的解決很多相關的圖論問題,如最大路徑問題等等。

因發明"深度優先搜尋演算法",霍普克洛夫特與陶爾揚共同獲得計算機領域的最高獎:圖靈獎.

和寬度優先搜尋類似,每當掃瞄已發現結點u的鄰接表從而發現新結點v時,深度優先搜尋將置v的先輩域π[v]為u。和寬度優先搜尋不同的是,前者的先輩子圖形成一棵樹,而後者產生的先輩子圖可以由幾棵樹組成,因為搜尋可能由多個源頂點開始重複進行。因此深度優先搜尋的先輩子圖的定義也和寬度優先搜尋稍有不同: gπ=(v,eπ),eπ=

深度優先搜尋的先輩子圖形成乙個由數個深度優先樹組成的深度優先森林。eπ中的邊稱為樹枝。

和寬度優先搜尋類似,深度優先在搜尋過程中也為結點著色以表示結點的狀態。每個頂點開始均為白色,搜尋中被發現時置為灰色,結束時又被置成黑色(即當其鄰接表被完全檢索之後)。這一技巧可以保證每一頂點搜尋結束時只存在於一棵深度優先樹上,因此這些樹都是分離的。

除了建立乙個深度優先森林外,深度優先搜尋同時為每個結點加蓋時間戳。每個結點v有兩個時間戳:當結點v第一次被發現(並置成灰色)時記錄下第乙個時間戳d[v],當結束檢查v的鄰接表時(並置v為黑色)記錄下第二個時間截f[v]。許多圖的演算法中都用到時間戳,他們對推算深度優先搜尋進**況是很有幫助的。

下列過程dfs記錄了何時在變數d[u]中發現結點u以及何時在變數f[u]中完成對結點u的檢索。這些時間戳為1到2|v|之間的整數,因為對每乙個v中結點都對應乙個發現事件和乙個完成事件。對每一頂點u,有 d[u](1) 在時刻d[u]前結點u為白色,在時刻d[u]和f[u]之間為灰色,以後就變為黑色。 下面的偽**就是乙個基本的深度優先搜尋演算法,輸人圖g可以是有向圖或無向圖,變數time是乙個全域性變數,用於記錄時間戳。

procedure dfs(g); - begin

- 1 for 每個頂點u∈v[g] do

- begin

- 2 color[u]←white;

- 3 π[u]←nil;

- end;

- 4 time←0;

- 5 for 每個頂點u∈v[g] do

- 6 if color[u]=white

- 7 then dfs_visit(g,u);

- end; -

- procedure dfs_visit(g,u);

- begin

- 1 color[u]←gray; δ白色結點u已被發現

- 3 for 每個頂點v∈adj[u] do δ探尋邊(u,v)

- 4 if color[v]=white

- then begin

- 5 π[v]←u;

- 6 dfs_visit(g,v);

- end;

- 7 color[u]←black; δ完成後置u為黑色

- 8 f[u]←time←time+1;

- end;

- 圖2說明了dfs在圖1所示的圖上執行的過程。被演算法探尋到的邊要麼為陰影覆蓋 (如果該邊為樹枝),要麼成虛線形式 (其他情況)。對於非樹枝的邊,分別標明b(或f)以表示反向邊、交叉邊或無向邊。我們用發現時刻z完成時刻的形式對結點加蓋時間戳。

圖2 深度優先搜尋演算法dfs在有向圖圖1上的執行過程

過程dfs執行如下。第1-3行把所有結點置為白色,所有π域初始化為nil。第4行復位全域性變數time,第5-7行依次檢索v中的結點,發現白色結點時,呼叫dfs_visit去訪問該結點。每次通過第7行呼叫dfs_visit時,結點u就成為深度優先森林中一棵新樹的根,當dfs返回時,每個結點u都對應於乙個發現時刻d[u]和乙個完成時刻f[u]。 每次開始呼叫dfs_visit(u)時結點u為白色,第1行置u為灰色,第2行使全域性時間變數增值並存於d[u]中,從而記錄下發現時刻d[u],第3-6行檢查和u相鄰接的每個頂點v,且若v為白色結點,則遞迴訪問結點v。在第3行語句中考慮到每乙個結點v∈adj[u]時,我們可以說邊(u,v)被深度優先搜尋探尋。最後當以u為起點的所有邊都被探尋後,第7-8行語句置u為黑色並記錄下完成時間f[u]。

演算法dfs執行時間的複雜性如何?dfs中第1-2行和5-7行的迴圈占用時間為o(v),這不包括執行呼叫dfs_visit過程語句所耗費的時間。事實上對每個頂點v∈v,過程dfs_visit僅被呼叫一次,因為dfs_visit僅適用於白色結點且過程首先進行的就是置結點為灰色,在dfs_visit(v)執行過程中,第3-6行的迴圈要執行|adj[v]|次。因為∑v∈v|adj[v]| =θ(e),因此執行過程dfs_visit中第2-5行語句占用的整個時間應為θ(e)。所以dfs的執行時間為θ(v+e)。

詳情請參考:

深度優先搜尋DFS

作為搜尋演算法的一種,dfs對於尋找乙個解的 np 包括npc 問題作用很大。但是,搜尋演算法畢竟是 時間複雜度是o n 的階乘級演算法,它的效率比較低,在資料規模變大時,這種演算法就顯得力不從心了。關於深度優先搜尋的效率問題,有多種解決方法。最具有通用性的是剪枝 prunning 也就是去除沒有用...

深度優先搜尋 DFS

深度優先搜尋 縮寫dfs 有點類似廣度優先搜尋,也是對乙個連通圖進行遍歷的演算法。它的思想是從乙個頂點v 0開始,沿著一條路一直走到底,如果發現不能到達目標解,那就返回到上乙個節點,然後從另一條路開始走到底,這種盡量往深處走的概念即是深度優先的概念。你可以跳過第二節先看第三節,還是引用上篇文章的樣例...

深度優先搜尋(dfs)

深度優先搜尋的一般步驟 1 從頂點v出發,訪問v。2 找出剛才訪問過的頂點的第乙個未被訪問的鄰接點,訪問該頂點。以該頂點為新頂點,重複此步驟,直到剛訪問的頂點沒有沒有未被訪問過的鄰接點為止。3 返回前乙個訪問過的仍有未被訪問過的鄰接點的頂點,找出該頂點的下乙個未被訪問過的鄰接點,訪問該頂點。4 重複...