BFS——广度优先算法(Breadth First Search)
1、前言
這幾天刷leetcode經常碰到DFS BFS的問題,之前一直也是模棱兩可,憑著感覺做,是需要總結一下了。
廣度優先搜索(也稱寬度優先搜索,縮寫BFS,以下采用廣度來描述)是連通圖的一種遍歷策略。因為它的思想是從一個頂點V0V0開始,輻射狀地優先遍歷其周圍較廣的區域,因此得名。
一般可以用它做什么呢?一個最直觀經典的例子就是走迷宮,我們從起點開始,找出到終點的最短路程,很多最短路徑算法就是基于廣度優先的思想成立的。
BFS的理論證明這里不作重點,有需要的童鞋可以自行查閱相關書籍,本博客主要以幾個例子說明一下BFS算法。
2、BFS的基本思路
?
上圖是連通圖,一般把頂點用ViVi表示,邊用EijEij表示。
2.1 算法的基本思路
常常我們有這樣一個問題:從一個起點開始要到一個終點,我們要找尋一條最短的路徑。以下圖為例,如果我們要求V0到V6的一條最短路(假設走一個節點按一步來算),我們明顯看出這條路徑就是V0?>V2?>V6V0?>V2?>V6,而不是V0?>V3?>V5?>V6V0?>V3?>V5?>V6。先想想你自己剛剛是怎么找到這條路徑的:首先看跟V0V0直接連接的節點V1、V2、V3V1、V2、V3,發現沒有V6V6,進而再看剛剛V1、V2、V3V1、V2、V3的直接連接節點分別是:{V0、V4V0、V4}、{V0、V1、V6V0、V1、V6}、{V0、V1、V5V0、V1、V5}(這里畫刪除線的意思是那些頂點在我們剛剛的搜索過程中已經找過了,我們不需要重新回頭再看他們了)。這時候我們從V2V2的連通節點集中找到了V6V6,那說明我們找到了這條V0到V6的最短路徑:V0?>V2?>V6V0?>V2?>V6,雖然你再進一步搜索V5的連接節點集合后會找到另一條路徑V0?>V3?>V5?>V6V0?>V3?>V5?>V6,但顯然他不是最短路徑,我們將其扔掉。
我們采用示例圖來說明這個過程,在搜索的過程中,初始所有節點是白色(代表了所有點都還沒開始搜索),把起點V0V0標志成灰色(表示即將輻射V0V0),下一步搜索的時候,我們把所有的灰色節點訪問一次,然后將其變成黑色(表示已經被輻射過了),進而再將他們所能到達的節點標志成灰色(因為那些節點是下一步搜索的目標點了),但是這里有個判斷,就像剛剛的例子,當訪問到V1V1節點的時候,它的下一個節點應該是V0V0和V4V4,但是V0V0已經在前面被染成黑色了,所以不會將它染灰色。這樣持續下去,直到目標節點V6V6被染灰色,說明了下一步就到終點了,沒必要再搜索(染色)其他節點了,此時可以結束搜索了,整個搜索就結束了。然后根據搜索過程,反過來把最短路徑找出來,下圖中把最終路徑上的節點標志成綠色。
整個過程如下圖所示:?
?
初始全部都是白色(未訪問)?
?
即將搜索起點V0(灰色)?
?
已搜索V0,即將搜索V1、V2、V3?
?
終點V6被染灰色,終止?
?
找到最短路徑
2.2 廣度優先搜索流程圖
3、BFS的核心代碼
/** * 廣度優先搜索 * @param Vs 起點 * @param Vd 終點 */ bool BFS(Node& Vs, Node& Vd){ queue<Node> Q; Node Vn, Vw; int i; //初始狀態將起點放進隊列Q Q.push(Vs); hash(Vw) = true;//設置節點已經訪問過了! while (!Q.empty()){//隊列不為空,繼續搜索! //取出隊列的頭Vn Vn = Q.front(); //從隊列中移除 Q.pop(); while(Vw = Vn通過某規則能夠到達的節點){ if (Vw == Vd){//找到終點了! //把路徑記錄,這里沒給出解法 return true;//返回 } if (isValid(Vw) && !visit[Vw]){ //Vw是一個合法的節點并且為白色節點 Q.push(Vw);//加入隊列Q hash(Vw) = true;//設置節點顏色 } } } return false;//無解 }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
對于一個題目來說,要標志節點是否訪問過,用數組是一種很快速的方法,但有時數據量太大,很難用一個大數組來記錄時,采用hash set是最好的做法。實際上visit數組在這里也是充當hash set的作用。
4、例1:Word Ladder (Leetcode 127 Medium)
?
問題描述:根據給定單詞,找出可以轉換的最短距離?
要求:?
①每次只能變換一個字母?
②每個字符串長度相同?
③每次轉換的字符串必須在wordList中?
④如果沒有這種轉換,返回0?
⑤沒有重復的?
思路說明:
- ① 構造一個字典,key為”ot”,”h_t”,”ho“這樣的形式,即可把滿足只變化一個字母的單詞歸并到同一個key的value中了。
- ② 運用BFS來做
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
5、例2:Word Break II (Leetcode 140 Hard)
?
問題描述:給定一個字符串和一個字典,判斷字典是否包含可以構成這個字符串的元素,如果有,返回由這些單詞組成的字符串,以列表形式表示。
思路說明:
- 這道題特別好,考察了BFS和DFS的聯合使用,下面給出我的思路,這道題還有很多可以優化的地方,但是主要是思路。
基于BFS的主要想法是:圖的頂點ViVi可以用每個單詞的第一個字母所在的索引來表示,圖的邊EijEij則用單詞表示。?
以nightmare為例,比如我們劃分成了night mare,那么圖應該為?
0 —> 5 —> 9。?
于是問題轉換成了,檢查是否有一條路徑從0到9(并列出所有的可能,需要用到DFS了)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
6、總結
假設圖有V個頂點,E條邊,廣度優先搜索算法需要搜索V個節點,因此這里的消耗是O(V),在搜索過程中,又需要根據邊來增加隊列的長度,于是這里需要消耗O(E),總得來說,效率大約是O(V+E)。?
其實最影響BFS算法的是在于Hash運算,我們前面給出了一個visit數組,已經算是最快的Hash了,但有些題目來說可能Hash的速度要退化到O(lgn)的復雜度,當然了,具體還是看實際情況的。?
BFS適合此類題目:給定初始狀態跟目標狀態,要求從初始狀態到目標狀態的最短路徑。
總結
以上是生活随笔為你收集整理的BFS——广度优先算法(Breadth First Search)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 深度学习优化器 optimizer 的选
- 下一篇: 机器学习基础——实现基本的决策树