從頭開(kāi)始復(fù)習(xí)算法之讓你徹徹底底的搞清楚BFS和DFS

最近又有點(diǎn)學(xué)不進(jìn)去了,不知道是不是天氣熱的緣故哈,沒(méi)辦法只好寫(xiě)一點(diǎn)算法來(lái)保持學(xué)習(xí)的路線不間斷咯。 關(guān)于BFS和DFS敢茁,這是我們?cè)诿嬖嚨臅r(shí)候經(jīng)常會(huì)遇到的兩個(gè)基礎(chǔ)算法,為什么說(shuō)基礎(chǔ)呢留美?因?yàn)樗斫饬酥蟛?0行左右的代碼彰檬,你說(shuō)基礎(chǔ)不基礎(chǔ)伸刃?

一、 BFS

BFS僧叉,全稱(chēng):Breadth First Search奕枝。中文翻譯為廣度優(yōu)先搜索或者是寬度優(yōu)先搜索,具體是怎么回事兒呢瓶堕?
首先來(lái)用下面一顆的樹(shù)來(lái)引入一下廣度優(yōu)先搜索的實(shí)現(xiàn)步驟:


如上圖所示隘道,我們先用一棵樹(shù)來(lái)引入廣度優(yōu)先搜索。為什么要用樹(shù)呢郎笆?因?yàn)槲矣X(jué)得樹(shù)來(lái)入門(mén)是最簡(jiǎn)單的谭梗,也是最容易理解的。
首先按照廣度優(yōu)先搜索的方法宛蚓,我們就應(yīng)該就是這樣來(lái)搜索:
A->B->C->D->E->F
或者是:
A->C->B->F->D->E

我先來(lái)從結(jié)果分析上面的遍歷結(jié)果激捏,我發(fā)現(xiàn)廣度優(yōu)先搜索有這樣的特點(diǎn)對(duì):

  • 先從根節(jié)點(diǎn)處查找,然后一層一層的往下查找凄吏。
    怎么理解呢远舅?首先查找A也就是第一層,然后再查找BC痕钢,也就是第二層图柏,最后查找DEF 也就是第三層。
  • 查找子層的時(shí)候任连,應(yīng)該按照父層的順序來(lái)查找子層蚤吹。
    怎么理解呢?首先查找A節(jié)點(diǎn)随抠,然后查找A的子層B和C裁着,當(dāng)然我們?cè)诓檎褹子層的時(shí)候先來(lái)查找的B節(jié)點(diǎn),那么在查找B的子節(jié)點(diǎn)的時(shí)候就要優(yōu)先查找B的子節(jié)點(diǎn)拱她。同理如果第二層里面先查找C也是一樣二驰。
  • 廣度優(yōu)先搜索的搜索結(jié)果并不唯一。

通過(guò)上面的結(jié)果來(lái)分析秉沼,其實(shí)我們很容易發(fā)現(xiàn)廣度優(yōu)先算法有點(diǎn)隊(duì)列的意味在里面桶雀。
怎么理解呢?來(lái)看下面一套圖:



首先新建一個(gè)隊(duì)列氧猬,先去查找一下樹(shù)的根結(jié)點(diǎn)背犯,并且將根結(jié)點(diǎn)A放入隊(duì)列中坏瘩。



移出節(jié)點(diǎn)A盅抚,并且把A的子節(jié)點(diǎn)加入到隊(duì)列中。

移出節(jié)點(diǎn)B倔矾,并且把B的子節(jié)點(diǎn)加入到隊(duì)列中妄均。

...
依次類(lèi)推柱锹,就將所謂的最后的廣度優(yōu)先搜索的路線給畫(huà)出來(lái)了:


最終結(jié)果

通過(guò)前面的探究,我們大概曉得了廣度優(yōu)先搜索的一個(gè)大致流程丰包,也差不多知道其實(shí)現(xiàn)的規(guī)則有點(diǎn)類(lèi)似于隊(duì)列禁熏。此時(shí)可能有人會(huì)說(shuō)了,你只是講解了一個(gè)簡(jiǎn)單的樹(shù)邑彪,而我們看到的BFS一般是用圖來(lái)展示的瞧毙。
針對(duì)上面的疑問(wèn),我們首先來(lái)畫(huà)一個(gè)無(wú)向圖寄症。

其實(shí)在我的理解里面:圖和樹(shù)最大的區(qū)別就是樹(shù)有專(zhuān)門(mén)的起點(diǎn)宙彪,但是圖卻沒(méi)有固定的起點(diǎn)。我們可以從任何一個(gè)點(diǎn)做起點(diǎn)來(lái)去搜索有巧,例如:從A點(diǎn)出發(fā)释漆,搜索結(jié)果是:
A->B->C->D->E->F
從E點(diǎn)出發(fā):
E-> C->D->F->A->B
...

這樣,我們其實(shí)就可以把之前樹(shù)的那一套類(lèi)似到圖中篮迎,只不過(guò)圖沒(méi)有起點(diǎn)罷了男图,并且將樹(shù)的子層換成了與指定節(jié)點(diǎn)相連的節(jié)點(diǎn)。這樣類(lèi)比之下也可以用隊(duì)列來(lái)實(shí)現(xiàn)甜橱。具體的代碼如下:

首先我們建立一張圖:

let graph = {
    'A':['B','C'],
    'B':['A','C','D'],
    'C':['A','B' ,'D','E'],
    'D':['B','C','E'],
    'E':['C','D','F'],
    'F':['E']
}

然后BFS的實(shí)現(xiàn)代碼如下:

function bfs(graph,startPoint){
    let queue = [];
    let result = []

    queue.push(startPoint);
    result.push(startPoint);

    while(queue.length>0){
        let point = queue.shift();
        let nodes = graph[point];
        for(let node of nodes){
            if(result.includes(node)) continue;
            result.push(node);
            stack.push(node);
        }
    }
    return result

}

二逊笆、DFS

好了,前面談到了廣度優(yōu)先搜索渗鬼,那么什么是深度優(yōu)先搜索呢览露?
首先我來(lái)用一套圖集來(lái)輔助理解深度優(yōu)先搜索的歷程:


首先選定起點(diǎn)為A(起點(diǎn)是任意的),然后從A出發(fā)譬胎,搜索到B



然后再?gòu)腂出發(fā)差牛,搜索到C



再?gòu)腃出發(fā)搜索到E

再?gòu)腅出發(fā)搜索到D,此時(shí)整張圖都沒(méi)有與D相連但是沒(méi)被搜索到的節(jié)點(diǎn)了堰乔,此時(shí)該怎么辦呢偏化?

如果搜索到?jīng)]有可以搜索的節(jié)點(diǎn),就應(yīng)該回溯到最近存在相鄰可以的節(jié)點(diǎn)處繼續(xù)搜索镐侯。

總之侦讨,我對(duì)于深度優(yōu)先搜索的理解就是:

  • 訪問(wèn)頂點(diǎn)A
  • 依次從A的未被訪問(wèn)的鄰接點(diǎn)出發(fā),對(duì)圖進(jìn)行深度優(yōu)先遍歷苟翻;直至圖中和A有路徑相通的頂點(diǎn)都被訪問(wèn)韵卤;
  • 若此時(shí)圖中尚有頂點(diǎn)未被訪問(wèn),則從一個(gè)未被訪問(wèn)的頂點(diǎn)出發(fā)崇猫,重新進(jìn)行深度優(yōu)先遍歷沈条,直到圖中所有頂點(diǎn)均被訪問(wèn)過(guò)為止。

這樣看來(lái)诅炉,深度優(yōu)先是不是有一點(diǎn)棧的意思在里面呢蜡歹?怎么理解哦屋厘,我們?cè)賮?lái)看下面一套圖。


首先我們從A出發(fā)月而,將節(jié)點(diǎn)A移入棧汗洒,然后將A移出棧



將A的子節(jié)點(diǎn)C,B移入棧內(nèi)父款。



然后將B移出棧溢谤,并將與B相連的D,C節(jié)點(diǎn)移入棧內(nèi)

然后將C移出棧憨攒,將與C相連的D溯香,E節(jié)點(diǎn)移入棧內(nèi)

然后將E移出棧,將與E相連的F浓恶,D節(jié)點(diǎn)移入棧內(nèi)



然后將D移除棧玫坛,我們發(fā)現(xiàn)并沒(méi)有可用的新節(jié)點(diǎn)了,就不再移入直接移出包晰。

移出F節(jié)點(diǎn)

當(dāng)我在移除新D節(jié)點(diǎn)的時(shí)候湿镀,發(fā)現(xiàn)D節(jié)點(diǎn)已經(jīng)被移出過(guò)了。此時(shí)我們就將該節(jié)點(diǎn)丟棄伐憾,同樣后面的節(jié)點(diǎn)也是一樣勉痴。最后就完成了深度優(yōu)先搜索。

通過(guò)上面的圖級(jí)演示是不是很容易就能看出來(lái)深度優(yōu)先搜索的實(shí)現(xiàn)原理呢树肃?下面我們用代碼的方式來(lái)去演示一下其原理吧:

function dfs(graph,startPoint){
    let stack = [];
    let result = []

    stack.push(startPoint);
    result.push(startPoint);

    while(stack.length>0){
        let point = stack.pop();
        let nodes = graph[point];
        for(let node of nodes){
            if(result.includes(node)) continue;
            result.push(node);
            stack.push(node);
        }
        

    }
    return result

}

說(shuō)在最后

說(shuō)了這么多蒸矛,感覺(jué)午休的時(shí)間都所剩無(wú)幾了,感覺(jué)自己還是沒(méi)有把這部分的內(nèi)容講清楚胸嘴。本來(lái)想額外寫(xiě)一點(diǎn)關(guān)于廣度優(yōu)先搜索的更深層次的用法的(作為很多圖形結(jié)構(gòu)的基礎(chǔ)雏掠,其實(shí)應(yīng)用還是比較廣的),但我還是需要睡的呀劣像!反正中午看來(lái)是說(shuō)不完了乡话。關(guān)于還沒(méi)寫(xiě)完的內(nèi)容呢,我這里提及一下:
如果相鄰節(jié)點(diǎn)之間的距離相同的情況下耳奕,我們想求廣度優(yōu)先搜索的最短路徑怎么來(lái)求呢绑青?
如果相鄰節(jié)點(diǎn)之間的距離不同呢?我們應(yīng)該如何來(lái)計(jì)算呢
如果明天中午有機(jī)會(huì)的話屋群,我會(huì)細(xì)細(xì)講解這一塊的內(nèi)容的闸婴,敬請(qǐng)期待哦!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末芍躏,一起剝皮案震驚了整個(gè)濱河市邪乍,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖溺欧,帶你破解...
    沈念sama閱讀 206,378評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異柏肪,居然都是意外死亡姐刁,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)烦味,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)聂使,“玉大人,你說(shuō)我怎么就攤上這事谬俄“匕校” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,702評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵溃论,是天一觀的道長(zhǎng)屎蜓。 經(jīng)常有香客問(wèn)我,道長(zhǎng)钥勋,這世上最難降的妖魔是什么炬转? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,259評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮算灸,結(jié)果婚禮上扼劈,老公的妹妹穿的比我還像新娘。我一直安慰自己菲驴,他們只是感情好荐吵,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著赊瞬,像睡著了一般先煎。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上巧涧,一...
    開(kāi)封第一講書(shū)人閱讀 49,036評(píng)論 1 285
  • 那天榨婆,我揣著相機(jī)與錄音,去河邊找鬼褒侧。 笑死良风,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的闷供。 我是一名探鬼主播烟央,決...
    沈念sama閱讀 38,349評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼歪脏!你這毒婦竟也來(lái)了疑俭?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 36,979評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤婿失,失蹤者是張志新(化名)和其女友劉穎钞艇,沒(méi)想到半個(gè)月后啄寡,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,469評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡哩照,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評(píng)論 2 323
  • 正文 我和宋清朗相戀三年挺物,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片飘弧。...
    茶點(diǎn)故事閱讀 38,059評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡识藤,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出次伶,到底是詐尸還是另有隱情痴昧,我是刑警寧澤,帶...
    沈念sama閱讀 33,703評(píng)論 4 323
  • 正文 年R本政府宣布冠王,位于F島的核電站赶撰,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏柱彻。R本人自食惡果不足惜扣囊,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望绒疗。 院中可真熱鬧侵歇,春花似錦、人聲如沸吓蘑。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,262評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)磨镶。三九已至溃蔫,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間琳猫,已是汗流浹背伟叛。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留脐嫂,地道東北人统刮。 一個(gè)月前我還...
    沈念sama閱讀 45,501評(píng)論 2 354
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像账千,于是被迫代替她去往敵國(guó)和親侥蒙。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評(píng)論 2 345