2022-02-22

什么是 BFS?

Breadth First Search (BFS) 寬度優(yōu)先搜索叶摄,又稱廣度優(yōu)先搜索属韧,是圖搜索算法中的一種,與 Depth First Search (DFS) 深度優(yōu)先搜索一起蛤吓,是面試中非常高頻且重要的算法之一宵喂。

BFS 顧名思義,我們搜索的過(guò)程是平鋪開進(jìn)行搜索会傲,即從起點(diǎn)開始锅棕,將所有和它相鄰的結(jié)點(diǎn)都搜索一遍拙泽,然后再一個(gè)個(gè)去搜索這些相鄰結(jié)點(diǎn)自己的相鄰節(jié)點(diǎn),一層一層鋪開裸燎,從而進(jìn)行搜索顾瞻。其搜索過(guò)程就像水中的漣漪,從中心開始德绿,向四周進(jìn)行擴(kuò)散荷荤,直到遍歷完整個(gè)圖。

BFS 為什么需要隊(duì)列移稳?

對(duì)于 BFS 算法蕴纳,正如上面所說(shuō)的,我們需要一層一層遍歷所有的相鄰結(jié)點(diǎn)个粱。那么相鄰結(jié)點(diǎn)之間的先后順序如何確定古毛?因此我們需要一個(gè)數(shù)據(jù)結(jié)構(gòu)來(lái)進(jìn)行存儲(chǔ)和操作,需要使得先遍歷的結(jié)點(diǎn)先被存儲(chǔ)几蜻,直到當(dāng)前層都被存儲(chǔ)后喇潘,按照先后順序,先被存儲(chǔ)的結(jié)點(diǎn)也會(huì)被先取出來(lái)梭稚,繼續(xù)遍歷它的相鄰結(jié)點(diǎn)颖低。

image

<figcaption>圖上的 BFS 搜索</figcaption>

因此我們可以發(fā)現(xiàn),這個(gè)需求不就是我們的隊(duì)列嗎弧烤,First In First Out (FIFO) 完全契合這里的 use case忱屑。因此對(duì)于 BFS 我們需要使用 Queue 這樣的一個(gè)數(shù)據(jù)結(jié)構(gòu),來(lái)存儲(chǔ)每一層的結(jié)點(diǎn)暇昂,同時(shí)維護(hù)『先進(jìn)先出 FIFO』的順序莺戒。

BFS 算法過(guò)程

BFS 的實(shí)現(xiàn)過(guò)程也非常直接,主要由 3 部分組成:

  • 起始:將起點(diǎn)(源點(diǎn)急波,樹的根節(jié)點(diǎn))放入隊(duì)列中
  • 擴(kuò)散:從隊(duì)列中取出隊(duì)頭的結(jié)點(diǎn)从铲,將它的相鄰結(jié)點(diǎn)放入隊(duì)列,不斷重復(fù)這一步
  • 終止:當(dāng)隊(duì)列為空時(shí)澄暮,說(shuō)明我們遍歷了所有的結(jié)點(diǎn)名段,整個(gè)圖都被搜索了一遍

下面我們以二維圖的 BFS 搜索過(guò)程,詳細(xì)圖解討論一下 BFS 的過(guò)程泣懊。

2D Matrix 上的 BFS

一般二維圖會(huì)用 2D Matrix 來(lái)表示伸辟,我們可以將二維矩陣進(jìn)行抽象 modelling:

  • 將每一個(gè)元素看作結(jié)點(diǎn)(Vertex)
  • 每一個(gè)元素和相鄰的上下左右四個(gè)方向的元素中間有一條邊(Edge)

因此,我們將矩陣上的搜索轉(zhuǎn)換為圖上的搜索馍刮。

圖片.png

對(duì)于這個(gè)圖信夫,是一個(gè)簡(jiǎn)單圖,每一條邊的權(quán)重 weight 都是 1,且每條邊都是無(wú)向邊静稻。對(duì)于簡(jiǎn)單圖警没,我們可以用 BFS 來(lái)尋找最短路徑的長(zhǎng)度。下面我們以這副圖為例振湾,來(lái)看一下我們?nèi)绾瓮ㄟ^(guò) BFS 來(lái)找到起始點(diǎn) 1終點(diǎn) 9的最短距離惠奸。

image

為了避免歧義,這里注釋一下恰梢。圖中黃色表示已經(jīng)搜索遍歷過(guò)的結(jié)點(diǎn)佛南,紅色表示現(xiàn)在正在搜索的結(jié)點(diǎn),粉色表示當(dāng)前層的結(jié)點(diǎn)嵌言,綠色表示從當(dāng)前層結(jié)點(diǎn)出發(fā)搜索到的相鄰結(jié)點(diǎn)嗅回,即下一層的結(jié)點(diǎn)。下面我們來(lái)圖解一下整個(gè)過(guò)程摧茴。

初始化搜索

首先绵载,queue.offer(M[0][0]) 我們先將起始點(diǎn)加入空隊(duì)列中,同時(shí)我們初始化我們的層數(shù) level = 1苛白,開始第一層的搜索娃豹。

https://vdn1.vzuu.com/SD/f37502c6-9361-11eb-99fe-1e63f535ca66.mp4?disable_local_cache=1&auth_key=1645516007-0-0-e19486f2f70d755098740fbcbfe57083&f=mp4&bu=pico&expiration=1645516007&v=hw

接下來(lái)按照算法,我們需要不斷從隊(duì)首取出來(lái)元素购裙,然后去搜索它的相鄰結(jié)點(diǎn)懂版。

第 0 層的搜索

首先通過(guò) queue.poll() 得到當(dāng)前的結(jié)點(diǎn)。

image

接下來(lái)將 M[0][0]上下左右四個(gè)相鄰結(jié)點(diǎn)加入到隊(duì)列中:

image

由于 1 的左和上都會(huì)出界躏率,沒(méi)有元素躯畴,因此添加完 2 和 7 后,就完成了當(dāng)前層的搜索薇芝。此時(shí) level 為 1蓬抄,表示 2 和 7 距離起點(diǎn) 1 只有 1 層,距離為 1夯到。更新層數(shù) level++嚷缭。

image

現(xiàn)在我們處理完了第 0 層的結(jié)點(diǎn),要開始處理第一層的結(jié)點(diǎn)耍贾。但是由于在第二層時(shí)我們還會(huì)在『找鄰居』的過(guò)程中找到第一層的結(jié)點(diǎn)阅爽,加入隊(duì)列,從而進(jìn)行了重復(fù)搜索逼争,因此我們需要一種方法來(lái)標(biāo)記某些結(jié)點(diǎn)被訪問(wèn)過(guò)优床。

一般我們使用一個(gè)和矩陣相同大小的二維 boolean array visited 來(lái)進(jìn)行 mark劝赔。每次找到一個(gè)鄰居時(shí)誓焦,先標(biāo)記該位置為『已訪問(wèn)過(guò)』,visited[x][y] = true,再將鄰居放入隊(duì)列中杂伟。

第 1 層結(jié)點(diǎn)的搜索

接下來(lái)我們發(fā)現(xiàn)了另一個(gè)問(wèn)題移层,對(duì)于第 0 層只有一個(gè)結(jié)點(diǎn),我們不需要注意赫粥。但是現(xiàn)在第 1 層有兩個(gè)結(jié)點(diǎn)观话,我們如何知道隊(duì)列中哪些是第 1 層的結(jié)點(diǎn),哪些是搜索得到的下一層的結(jié)點(diǎn)呢越平?

int size = queue.size()

我們可以在搜索當(dāng)前層之前频蛔,獲得當(dāng)前隊(duì)列的大小,因?yàn)?strong>當(dāng)前狀態(tài)下隊(duì)列中的元素都是當(dāng)前層的結(jié)點(diǎn)秦叛。通過(guò)隊(duì)列的 size 進(jìn)行限制晦溪,從而將當(dāng)前層 poll 的次數(shù)限制在 size 次,從而區(qū)分開當(dāng)前層和下一層的結(jié)點(diǎn)挣跋。

image

將 2 的相鄰結(jié)點(diǎn) 3 和 8 放入隊(duì)列三圆,1 由于已經(jīng)訪問(wèn)過(guò)了,不會(huì)再加入隊(duì)列中避咆。

image

接下來(lái)處理當(dāng)前層的第二個(gè)結(jié)點(diǎn) 7舟肉,由于 7 也是當(dāng)前層的最后一個(gè)結(jié)點(diǎn),因此當(dāng)它被從隊(duì)列中 poll 出來(lái)后查库,隊(duì)列中就全都是下一層的結(jié)點(diǎn)了路媚。

image

由于 8 已經(jīng)被訪問(wèn)過(guò)了,也不會(huì)再重復(fù)放入隊(duì)列中樊销,當(dāng)把 13 放入隊(duì)列后磷籍,當(dāng)前層的搜索也已經(jīng)結(jié)束。此時(shí) level = 2 表示 2 和 7 到起點(diǎn)的距離是 2现柠,更新層數(shù) level++院领。

image

結(jié)束

對(duì)于最后一層,我們從 3 開始搜索鄰居够吩,當(dāng)我們遇到 9 的時(shí)候比然,我們找到了終點(diǎn),因此搜索結(jié)束周循。

此時(shí) level 值為 3强法,表示終點(diǎn) 9 到起點(diǎn) 1 的距離為 3,也就是我們最后的答案湾笛。

image

對(duì)于整個(gè)搜索過(guò)程可以看下面的視頻:

<iframe title="video" src="https://video.zhihu.com/video/1361277881042296833?player=%7B%22shouldShowPageFullScreenButton%22%3Atrue%7D" allowfullscreen="" class="css-uwwqev" frameborder="0"></iframe>

BFS搜索過(guò)程

代碼

下面來(lái)看一下代碼饮怯,正如我們前面所討論的,我們使用了 size 以及 level 兩個(gè)變量來(lái)維護(hù)搜索過(guò)程中的當(dāng)前層以及當(dāng)前層到起點(diǎn)的距離嚎研,以及使用 visited 數(shù)組進(jìn)行標(biāo)記訪問(wèn)過(guò)的結(jié)點(diǎn)避免重復(fù)訪問(wèn)蓖墅。

代碼非常簡(jiǎn)單,一目了然:

image

這里我們使用了自定義的 Class Cell 來(lái)表示每一個(gè)結(jié)點(diǎn)的橫坐標(biāo)和縱坐標(biāo),我們也可以使用一個(gè)長(zhǎng)度為 2 的數(shù)組來(lái)表示论矾,index 0 表示 x教翩,index 1 表示 y。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末贪壳,一起剝皮案震驚了整個(gè)濱河市饱亿,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌闰靴,老刑警劉巖彪笼,帶你破解...
    沈念sama閱讀 211,743評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異蚂且,居然都是意外死亡杰扫,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,296評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門膘掰,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)章姓,“玉大人,你說(shuō)我怎么就攤上這事识埋》惨粒” “怎么了?”我有些...
    開封第一講書人閱讀 157,285評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵窒舟,是天一觀的道長(zhǎng)系忙。 經(jīng)常有香客問(wèn)我,道長(zhǎng)惠豺,這世上最難降的妖魔是什么银还? 我笑而不...
    開封第一講書人閱讀 56,485評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮洁墙,結(jié)果婚禮上蛹疯,老公的妹妹穿的比我還像新娘。我一直安慰自己热监,他們只是感情好捺弦,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,581評(píng)論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著孝扛,像睡著了一般列吼。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上苦始,一...
    開封第一講書人閱讀 49,821評(píng)論 1 290
  • 那天寞钥,我揣著相機(jī)與錄音,去河邊找鬼陌选。 笑死理郑,一個(gè)胖子當(dāng)著我的面吹牛蹄溉,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播香浩,決...
    沈念sama閱讀 38,960評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼臼勉!你這毒婦竟也來(lái)了邻吭?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,719評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤宴霸,失蹤者是張志新(化名)和其女友劉穎囱晴,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體瓢谢,經(jīng)...
    沈念sama閱讀 44,186評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡畸写,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,516評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了氓扛。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片枯芬。...
    茶點(diǎn)故事閱讀 38,650評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖采郎,靈堂內(nèi)的尸體忽然破棺而出千所,到底是詐尸還是另有隱情,我是刑警寧澤蒜埋,帶...
    沈念sama閱讀 34,329評(píng)論 4 330
  • 正文 年R本政府宣布淫痰,位于F島的核電站,受9級(jí)特大地震影響整份,放射性物質(zhì)發(fā)生泄漏待错。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,936評(píng)論 3 313
  • 文/蒙蒙 一烈评、第九天 我趴在偏房一處隱蔽的房頂上張望火俄。 院中可真熱鬧,春花似錦讲冠、人聲如沸烛占。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,757評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)忆家。三九已至,卻和暖如春德迹,著一層夾襖步出監(jiān)牢的瞬間芽卿,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,991評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工胳搞, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留卸例,地道東北人称杨。 一個(gè)月前我還...
    沈念sama閱讀 46,370評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像筷转,于是被迫代替她去往敵國(guó)和親姑原。 傳聞我的和親對(duì)象是個(gè)殘疾皇子亚侠,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,527評(píng)論 2 349

推薦閱讀更多精彩內(nèi)容

  • 算法思想貪心思想雙指針排序快速選擇堆排序桶排序荷蘭國(guó)旗問(wèn)題二分查找搜索BFSDFSBacktracking分治動(dòng)態(tài)...
    第六象限閱讀 3,065評(píng)論 0 0
  • 1.鏈表 1.實(shí)現(xiàn)一個(gè)單向鏈表 2.找出鏈表相交節(jié)點(diǎn)昼伴,假設(shè)均沒(méi)有環(huán) 3.判斷鏈表是否有環(huán)思路:使用快慢兩個(gè)指針激才,當(dāng)...
    X1028閱讀 656評(píng)論 0 0
  • 動(dòng)態(tài)規(guī)劃 111. 爬樓梯思路類似斐波那契數(shù)列注意考慮第 0 階的特殊情況 272. 爬樓梯 II思路類似上題存和,只...
    6默默Welsh閱讀 2,418評(píng)論 0 1
  • 遞歸 一棵樹要么是空樹母截,要么有兩個(gè)指針肤视,每個(gè)指針指向一棵樹斋攀。樹是一種遞歸結(jié)構(gòu)捐下,很多樹的問(wèn)題可以使用遞歸來(lái)處理到腥。 1...
    奔向星辰大海閱讀 796評(píng)論 0 0
  • 1)這本書為什么值得看: Python語(yǔ)言描述朵逝,如果學(xué)的Python用這本書學(xué)數(shù)據(jù)結(jié)構(gòu)更合適 2016年出版,內(nèi)容...
    孫懷闊閱讀 12,456評(píng)論 0 15