图的广度优先搜索(bfs)以及深度优先搜索(dfs)
1.前言
和樹的遍歷類似,圖的遍歷也是從圖中某點出發,然后按照某種方法對圖中所有頂點進行訪問,且僅訪問一次。
但是圖的遍歷相對樹而言要更為復雜。因為圖中的任意頂點都可能與其他頂點相鄰,所以在圖的遍歷中必須記錄已被訪問的頂點,避免重復訪問。
根據搜索路徑的不同,我們可以將遍歷圖的方法分為兩種:廣度優先搜索和深度優先搜索。
2.圖的基本概念
無向圖:頂點對(u,v)是無序的,即(u,v)和(v,u)是同一條邊。常用一對圓括號表示。如圖所示:
有向圖:頂點對<u,v>是有序的,它是指從頂點u到頂點 v的一條有向邊。其中u是有向邊的始點,v是有向邊的終點。常用一對<>表示。如圖所示:
連通圖:在無向圖G中,從頂點v到頂點v’有路徑,則稱v和v’是聯通的。若圖中任意兩頂點v、v’∈V,v和v’之間均聯通,則稱G是連通圖。上述兩圖均為連通圖。如圖所示:
非連通圖:若無向圖G中,存在v和v’之間不連通,則稱G是非連通圖。如圖所示:
3.廣度優先搜索
基本思想:寬度優先搜索算法(又稱廣度優先搜索)是最簡便的圖的搜索算法之一,這一算法也是很多重要的圖的算法的原型。Dijkstra單源最短路徑算法和Prim最小生成樹算法都采用了和寬度優先搜索類似的思想。其別名又叫BFS,屬于一種盲目搜尋法,目的是系統地展開并檢查圖中的所有節點,以找尋結果。換句話說,它并不考慮結果的可能位置,徹底地搜索整張圖,直到找到結果為止。
廣度優先搜索算法的搜索步驟一般是:
(1)從隊列頭取出一個結點,檢查它按照擴展規則是否能夠擴展,如果能則產生一個新結點。
(2)檢查新生成的結點,看它是否已在隊列中存在,如果新結點已經在隊列中出現過,就放棄這個結點,然后回到第(1)步。否則,如果新結點未曾在隊列中出現過,則將它加入到隊列尾。
(3)檢查新結點是否目標結點。如果新結點是目標結點,則搜索成功,程序結束;若新結點不是目標結點,則回到第(1)步,再從隊列頭取出結點進行擴展。
最終可能產生兩種結果:找到目標結點,或擴展完所有結點而沒有找到目標結點。
如果目標結點存在于解答樹的有限層上,廣度優先搜索算法一定能保證找到一條通向它的最佳路徑,因此廣度優先搜索算法特別適用于只需求出最優解的問題。當問題需要給出解的路徑,則要保存每個結點的來源,也就是它是從哪一個節點擴展來的。
對于廣度優先搜索算法來說,問題不同則狀態結點的結構和結點擴展規則是不同的,但搜索的策略是相同的。
廣度優先搜索如圖所示:
1.初始時全部頂點均未被訪問,visited數組初始化為0,隊列中沒有元素。
2.即將訪問頂點v0。
3.訪問頂點v0,并置visited[0]的值為1,同時將v0入隊。
4.將v0出隊,訪問v0的鄰接點v2。判斷visited[2],因為visited[2]的值為0,訪問v2。
5.將visited[2]置為1,并將v2入隊。
6.訪問v0鄰接點v1。判斷visited[1],因為visited[1]的值為0,訪問v1。
7.將visited[1]置為0,并將v1入隊。
8.判斷visited[3],因為它的值為0,訪問v3。將visited[3]置為0,并將v3入隊。
9.v0的全部鄰接點均已被訪問完畢。將隊頭元素v2出隊,開始訪問v2的所有鄰接點。
開始訪問v2鄰接點v0,判斷visited[0],因為其值為1,不進行訪問。
繼續訪問v2鄰接點v4,判斷visited[4],因為其值為0,訪問v4,如下圖:
10.將visited[4]置為1,并將v4入隊。
11.v2的全部鄰接點均已被訪問完畢。將隊頭元素v1出隊,開始訪問v1的所有鄰接點。
開始訪問v1鄰接點v0,因為visited[0]值為1,不進行訪問。
繼續訪問v1鄰接點v4,因為visited[4]的值為1,不進行訪問。
繼續訪問v1鄰接點v5,因為visited[5]值為0,訪問v5,如下圖:
12.將visited[5]置為1,并將v5入隊。
13.v1的全部鄰接點均已被訪問完畢,將隊頭元素v3出隊,開始訪問v3的所有鄰接點。
開始訪問v3鄰接點v0,因為visited[0]值為1,不進行訪問。
繼續訪問v3鄰接點v5,因為visited[5]值為1,不進行訪問。
14.v3的全部鄰接點均已被訪問完畢,將隊頭元素v4出隊,開始訪問v4的所有鄰接點。
開始訪問v4的鄰接點v2,因為visited[2]的值為1,不進行訪問。
繼續訪問v4的鄰接點v6,因為visited[6]的值為0,訪問v6,如下圖:
15.將visited[6]值為1,并將v6入隊。
16.v4的全部鄰接點均已被訪問完畢,將隊頭元素v5出隊,開始訪問v5的所有鄰接點。
開始訪問v5鄰接點v3,因為visited[3]的值為1,不進行訪問。
繼續訪問v5鄰接點v6,因為visited[6]的值為1,不進行訪問
17.v5的全部鄰接點均已被訪問完畢,將隊頭元素v6出隊,開始訪問v6的所有鄰接點。
開始訪問v6鄰接點v4,因為visited[4]的值為1,不進行訪問。
繼續訪問v6鄰接點v5,因為visited[5]的值文1,不進行訪問。
18.隊列為空,退出循環,全部頂點均訪問完畢。
廣度優先搜索例題:
【例一】 有一個寬為W、高為H的矩形平面,用黑色和紅色兩種顏色的方磚鋪滿。一個小朋友站在一塊黑色方塊上開始移動,規定移動方向有上、下、左、右四種,且只能在黑色方塊上移動(即不能移到紅色方塊上)。編寫一個程序,計算小朋友從起點出發可到達的所有黑色方磚的塊數(包括起點)。
例如,如圖1所示的矩形平面中,“#”表示紅色磚塊,“.”表示黑色磚塊,“@”表示小朋友的起點,則小朋友能走到的黑色方磚有28塊。
代碼如下:
4.深度優先搜索
基本思想:深度優先搜索是一種在開發爬蟲早期使用較多的方法。它的目的是要達到被搜索結構的葉結點(即那些不包含任何超鏈的HTML文件) 。在一個HTML文件中,當一個超鏈被選擇后,被鏈接的HTML文件將執行深度優先搜索,即在搜索其余的超鏈結果之前必須先完整地搜索單獨的一條鏈。深度優先搜索沿著HTML文件上的超鏈走到不能再深入為止,然后返回到某一個HTML文件,再繼續選擇該HTML文件中的其他超鏈。當不再有其他超鏈可選擇時,說明搜索已經結束。
深度優先搜索DFS(Depth First Search)是從初始結點開始擴展,擴展順序總是先擴展最新產生的結點。這就使得搜索沿著狀態空間某條單一的路徑進行下去,直到最后的結點不能產生新結點或者找到目標結點為止。當搜索到不能產生新的結點的時候,就沿著結點產生順序的反方向尋找可以產生新結點的結點,并擴展它,形成另一條搜索路徑。
為了便于進行搜索,要設置一個表存儲所有的結點。由于在深度優先搜索算法中,要滿足先生成的結點后擴展的原則,所以存儲結點的表一般采用棧這種數據結構。
深度優先搜索算法的搜索步驟一般是:
(1)從初始結點開始,將待擴展結點依次放到棧中。
(2)如果???#xff0c;即所有待擴展結點已全部擴展完畢,則問題無解,退出。
(3)取棧中最新加入的結點,即棧頂結點出棧,并用相應的擴展原則擴展出所有的子結點,并按順序將這些結點放入棧中。若沒有子結點產生,則轉(2)。
(4)如果某個子結點為目標結點,則找到問題的解(這不一定是最優解),結束。如果要求得問題的最優解,或者所有解,則轉(2),繼續搜索新的目標結點。
深度優先搜索如圖所示:
1.初始時所有頂點均未被訪問,visited數組為空。
2.即將訪問v0。
3.訪問v0,并將visited[0]的值置為1。
4.訪問v0的鄰接點v2,判斷visited[2],因其值為0,訪問v2。
5.將visited[2]置為1。
6.訪問v2的鄰接點v0,判斷visited[0],其值為1,不訪問。
繼續訪問v2的鄰接點v4,判斷visited[4],其值為0,訪問v4。
7.將visited[4]置為1。
8.訪問v4的鄰接點v1,判斷visited[1],其值為0,訪問v1。
9.將visited[1]置為1。
10.訪問v1的鄰接點v0,判斷visited[0],其值為1,不訪問。
繼續訪問v1的鄰接點v4,判斷visited[4],其值為1,不訪問。
繼續訪問v1的鄰接點v5,判讀visited[5],其值為0,訪問v5。
11.將visited[5]置為1。
12.訪問v5的鄰接點v1,判斷visited[1],其值為1,不訪問。
繼續訪問v5的鄰接點v3,判斷visited[3],其值為0,訪問v3。
13.將visited[1]置為1。
14.訪問v3的鄰接點v0,判斷visited[0],其值為1,不訪問。
繼續訪問v3的鄰接點v5,判斷visited[5],其值為1,不訪問。
v3所有鄰接點均已被訪問,回溯到其上一個頂點v5,遍歷v5所有鄰接點。
訪問v5的鄰接點v6,判斷visited[6],其值為0,訪問v6。
15.將visited[6]置為1。
16.訪問v6的鄰接點v4,判斷visited[4],其值為1,不訪問。
訪問v6的鄰接點v5,判斷visited[5],其值為1,不訪問。
v6所有鄰接點均已被訪問,回溯到其上一個頂點v5,遍歷v5剩余鄰接點。
17.v5所有鄰接點均已被訪問,回溯到其上一個頂點v1。
v1所有鄰接點均已被訪問,回溯到其上一個頂點v4,遍歷v4剩余鄰接點v6。
v4所有鄰接點均已被訪問,回溯到其上一個頂點v2。
v2所有鄰接點均已被訪問,回溯到其上一個頂點v1,遍歷v1剩余鄰接點v3。
v1所有鄰接點均已被訪問,搜索結束。
深度優先搜索例題:
【例1】黑色方塊
有一個寬為W、高為H的矩形平面,用黑色和紅色兩種顏色的方磚鋪滿。一個小朋友站在一塊黑色方塊上開始移動,規定移動方向有上、下、左、右四種,且只能在黑色方塊上移動(即不能移到紅色方塊上)。編寫一個程序,計算小朋友從起點出發可到達的所有黑色方磚的塊數(包括起點)。
例如,如圖1所示的矩形平面中,“#”表示紅色磚塊,“.”表示黑色磚塊,“@”表示小朋友的起點,則小朋友能走到的黑色方磚有28塊。
代碼如下:
對于同樣的問題,如果深搜和廣搜都可以的話,推薦使用廣搜,因為廣搜不用遞歸調用系統棧,所以它的速度更快一些。
參考博客:
https://blog.csdn.net/weixin_40953222/article/details/80544928
https://www.cnblogs.com/cs-whut/p/11147213.html
努力加油a啊,(o)/~
總結
以上是生活随笔為你收集整理的图的广度优先搜索(bfs)以及深度优先搜索(dfs)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Common Number(奇偶二分+找
- 下一篇: 二分搜索(折半搜索),lower_bou