圖的基本算法(BFS和DFS)

圖是一種靈活的數(shù)據(jù)結(jié)構(gòu),一般作為一種模型用來(lái)定義對(duì)象之間的關(guān)系或聯(lián)系。對(duì)象由頂點(diǎn)(V)表示,而對(duì)象之間的關(guān)系或者關(guān)聯(lián)則通過(guò)圖的邊(E)來(lái)表示螟加。
圖可以分為有向圖和無(wú)向圖,一般用G=(V,E)來(lái)表示圖。經(jīng)常用鄰接矩陣或者鄰接表來(lái)描述一副圖捆探。
在圖的基本算法中然爆,最初需要接觸的就是圖的遍歷算法,根據(jù)訪問(wèn)節(jié)點(diǎn)的順序黍图,可分為廣度優(yōu)先搜索(BFS)和深度優(yōu)先搜索(DFS)曾雕。


廣度優(yōu)先搜索(BFS)
廣度優(yōu)先搜索在進(jìn)一步遍歷圖中頂點(diǎn)之前,先訪問(wèn)當(dāng)前頂點(diǎn)的所有鄰接結(jié)點(diǎn)助被。
a .首先選擇一個(gè)頂點(diǎn)作為起始結(jié)點(diǎn)剖张,并將其染成灰色,其余結(jié)點(diǎn)為白色揩环。
b. 將起始結(jié)點(diǎn)放入隊(duì)列中搔弄。
c. 從隊(duì)列首部選出一個(gè)頂點(diǎn),并找出所有與之鄰接的結(jié)點(diǎn)丰滑,將找到的鄰接結(jié)點(diǎn)放入隊(duì)列尾部顾犹,將已訪問(wèn)過(guò)結(jié)點(diǎn)涂成黑色,沒(méi)訪問(wèn)過(guò)的結(jié)點(diǎn)是白色吨枉。如果頂點(diǎn)的顏色是灰色蹦渣,表示已經(jīng)發(fā)現(xiàn)并且放入了隊(duì)列,如果頂點(diǎn)的顏色是白色貌亭,表示還沒(méi)有發(fā)現(xiàn)
d. 按照同樣的方法處理隊(duì)列中的下一個(gè)結(jié)點(diǎn)。
基本就是出隊(duì)的頂點(diǎn)變成黑色认臊,在隊(duì)列里的是灰色圃庭,還沒(méi)入隊(duì)的是白色。
用一副圖來(lái)表達(dá)這個(gè)流程如下:

1.初始狀態(tài)失晴,從頂點(diǎn)1開(kāi)始剧腻,隊(duì)列={1}
2.訪問(wèn)1的鄰接頂點(diǎn),1出隊(duì)變黑涂屁,2,3入隊(duì)书在,隊(duì)列={2,3,}
3.訪問(wèn)2的鄰接結(jié)點(diǎn),2出隊(duì)拆又,4入隊(duì)儒旬,隊(duì)列={3,4}
4.訪問(wèn)3的鄰接結(jié)點(diǎn),3出隊(duì)帖族,隊(duì)列={4}
5.訪問(wèn)4的鄰接結(jié)點(diǎn)栈源,4出隊(duì),隊(duì)列={ 空}

從頂點(diǎn)1開(kāi)始進(jìn)行廣度優(yōu)先搜索:

  1. 初始狀態(tài)竖般,從頂點(diǎn)1開(kāi)始甚垦,隊(duì)列={1}
  2. 訪問(wèn)1的鄰接頂點(diǎn),1出隊(duì)變黑,2,3入隊(duì)艰亮,隊(duì)列={2,3,}
  3. 訪問(wèn)2的鄰接結(jié)點(diǎn)闭翩,2出隊(duì),4入隊(duì)迄埃,隊(duì)列={3,4}
  4. 訪問(wèn)3的鄰接結(jié)點(diǎn)疗韵,3出隊(duì),隊(duì)列={4}
  5. 訪問(wèn)4的鄰接結(jié)點(diǎn)调俘,4出隊(duì)伶棒,隊(duì)列={ 空}
    結(jié)點(diǎn)5對(duì)于1來(lái)說(shuō)不可達(dá)。
    上面的圖可以通過(guò)如下鄰接矩陣表示:
int maze[5][5] = {
    { 0, 1, 1, 0, 0 },
    { 0, 0, 1, 1, 0 },
    { 0, 1, 1, 1, 0 },
    { 1, 0, 0, 0, 0 },
    { 0, 0, 1, 1, 0 }
};

BFS核心代碼如下:

#include <iostream>
#include <queue>
#define N 5
using namespace std;
int maze[N][N] = {
    { 0, 1, 1, 0, 0 },
    { 0, 0, 1, 1, 0 },
    { 0, 1, 1, 1, 0 },
    { 1, 0, 0, 0, 0 },
    { 0, 0, 1, 1, 0 }
};
int visited[N + 1] = { 0, };
void BFS(int start)
{
    queue<int> Q;
    Q.push(start);
    visited[start] = 1;
    while (!Q.empty())
    {
        int front = Q.front();
        cout << front << " ";
        Q.pop();
        for (int i = 1; i <= N; i++)
        {
            if (!visited[i] && maze[front - 1][i - 1] == 1)
            {
                visited[i] = 1;
                Q.push(i);
            }
        }
    }
}
int main()
{
    for (int i = 1; i <= N; i++)
    {
        if (visited[i] == 1)
            continue;
        BFS(i);
    }
    return 0;
}

深度優(yōu)先搜索(DFS)
深度優(yōu)先搜索在搜索過(guò)程中訪問(wèn)某個(gè)頂點(diǎn)后彩库,需要遞歸地訪問(wèn)此頂點(diǎn)的所有未訪問(wèn)過(guò)的相鄰頂點(diǎn)肤无。
初始條件下所有節(jié)點(diǎn)為白色,選擇一個(gè)作為起始頂點(diǎn)骇钦,按照如下步驟遍歷:
a. 選擇起始頂點(diǎn)涂成灰色宛渐,表示還未訪問(wèn)
b. 從該頂點(diǎn)的鄰接頂點(diǎn)中選擇一個(gè),繼續(xù)這個(gè)過(guò)程(即再尋找鄰接結(jié)點(diǎn)的鄰接結(jié)點(diǎn))眯搭,一直深入下去窥翩,直到一個(gè)頂點(diǎn)沒(méi)有鄰接結(jié)點(diǎn)了,涂黑它鳞仙,表示訪問(wèn)過(guò)了
c. 回溯到這個(gè)涂黑頂點(diǎn)的上一層頂點(diǎn)寇蚊,再找這個(gè)上一層頂點(diǎn)的其余鄰接結(jié)點(diǎn),繼續(xù)如上操作棍好,如果所有鄰接結(jié)點(diǎn)往下都訪問(wèn)過(guò)了仗岸,就把自己涂黑,再回溯到更上一層借笙。
d. 上一層繼續(xù)做如上操作扒怖,知道所有頂點(diǎn)都訪問(wèn)過(guò)。
用圖可以更清楚的表達(dá)這個(gè)過(guò)程:

1.初始狀態(tài)业稼,從頂點(diǎn)1開(kāi)始

2.依次訪問(wèn)過(guò)頂點(diǎn)1,2,3后盗痒,終止于頂點(diǎn)3

3.從頂點(diǎn)3回溯到頂點(diǎn)2,繼續(xù)訪問(wèn)頂點(diǎn)5低散,并且終止于頂點(diǎn)5

4.從頂點(diǎn)5回溯到頂點(diǎn)2俯邓,并且終止于頂點(diǎn)2

5.從頂點(diǎn)2回溯到頂點(diǎn)1,并終止于頂點(diǎn)1

6.從頂點(diǎn)4開(kāi)始訪問(wèn)谦纱,并終止于頂點(diǎn)4

從頂點(diǎn)1開(kāi)始做深度搜索:

  1. 初始狀態(tài)看成,從頂點(diǎn)1開(kāi)始
  2. 依次訪問(wèn)過(guò)頂點(diǎn)1,2,3后,終止于頂點(diǎn)3
  3. 從頂點(diǎn)3回溯到頂點(diǎn)2跨嘉,繼續(xù)訪問(wèn)頂點(diǎn)5川慌,并且終止于頂點(diǎn)5
  4. 從頂點(diǎn)5回溯到頂點(diǎn)2吃嘿,并且終止于頂點(diǎn)2
  5. 從頂點(diǎn)2回溯到頂點(diǎn)1,并終止于頂點(diǎn)1
  6. 從頂點(diǎn)4開(kāi)始訪問(wèn)梦重,并終止于頂點(diǎn)4

上面的圖可以通過(guò)如下鄰接矩陣表示:

int maze[5][5] = {
    { 0, 1, 1, 0, 0 },
    { 0, 0, 1, 0, 1 },
    { 0, 0, 1, 0, 0 },
    { 1, 1, 0, 0, 1 },
    { 0, 0, 1, 0, 0 }
};

DFS核心代碼如下(遞歸實(shí)現(xiàn)):

#include <iostream>
#define N 5
using namespace std;
int maze[N][N] = {
    { 0, 1, 1, 0, 0 },
    { 0, 0, 1, 0, 1 },
    { 0, 0, 1, 0, 0 },
    { 1, 1, 0, 0, 1 },
    { 0, 0, 1, 0, 0 }
};
int visited[N + 1] = { 0, };
void DFS(int start)
{
    visited[start] = 1;
    for (int i = 1; i <= N; i++)
    {
        if (!visited[i] && maze[start - 1][i - 1] == 1)
            DFS(i);
    }
    cout << start << " ";
}
int main()
{
    for (int i = 1; i <= N; i++)
    {
        if (visited[i] == 1)
            continue;
        DFS(i);
    }
    return 0;
}

非遞歸實(shí)現(xiàn)如下兑燥,借助一個(gè)棧:

#include <iostream>
#include <stack>
#define N 5
using namespace std;
int maze[N][N] = {
    { 0, 1, 1, 0, 0 },
    { 0, 0, 1, 0, 1 },
    { 0, 0, 1, 0, 0 },
    { 1, 1, 0, 0, 1 },
    { 0, 0, 1, 0, 0 }
};
int visited[N + 1] = { 0, };
void DFS(int start)
{
    stack<int> s;
    s.push(start);
    visited[start] = 1;
    bool is_push = false;
    while (!s.empty())
    {
        is_push = false;
        int v = s.top();
        for (int i = 1; i <= N; i++)
        {
            if (maze[v - 1][i - 1] == 1 && !visited[i])
            {
                visited[i] = 1;
                s.push(i);
                is_push = true;
                break;
            }
        }
        if (!is_push)
        {
            cout << v << " ";
            s.pop();
        }

    }
}
int main()
{
    for (int i = 1; i <= N; i++)
    {
        if (visited[i] == 1)
            continue;
        DFS(i);
    }
    return 0;
}

有的DFS是先訪問(wèn)讀取到的結(jié)點(diǎn),等回溯時(shí)就不再輸出該結(jié)點(diǎn)琴拧,也是可以的降瞳。算法和我上面的區(qū)別就是輸出點(diǎn)的時(shí)機(jī)不同,思想還是一樣的蚓胸。DFS在環(huán)監(jiān)測(cè)和拓?fù)渑判蛑卸加胁诲e(cuò)的應(yīng)用挣饥。

PS: 圖文均為本人原創(chuàng),畫(huà)了好幾個(gè)小時(shí)沛膳,轉(zhuǎn)載注明出處扔枫,尊重知識(shí)勞動(dòng),謝謝~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末锹安,一起剝皮案震驚了整個(gè)濱河市短荐,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌叹哭,老刑警劉巖忍宋,帶你破解...
    沈念sama閱讀 206,968評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異风罩,居然都是意外死亡糠排,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門超升,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)乳讥,“玉大人,你說(shuō)我怎么就攤上這事廓俭。” “怎么了唉工?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,220評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵研乒,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我淋硝,道長(zhǎng)雹熬,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,416評(píng)論 1 279
  • 正文 為了忘掉前任谣膳,我火速辦了婚禮竿报,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘继谚。我一直安慰自己烈菌,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著芽世,像睡著了一般挚赊。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上济瓢,一...
    開(kāi)封第一講書(shū)人閱讀 49,144評(píng)論 1 285
  • 那天荠割,我揣著相機(jī)與錄音,去河邊找鬼旺矾。 笑死蔑鹦,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的箕宙。 我是一名探鬼主播嚎朽,決...
    沈念sama閱讀 38,432評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼扒吁!你這毒婦竟也來(lái)了火鼻?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,088評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤雕崩,失蹤者是張志新(化名)和其女友劉穎魁索,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體盼铁,經(jīng)...
    沈念sama閱讀 43,586評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡粗蔚,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了饶火。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片鹏控。...
    茶點(diǎn)故事閱讀 38,137評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖肤寝,靈堂內(nèi)的尸體忽然破棺而出当辐,到底是詐尸還是另有隱情,我是刑警寧澤鲤看,帶...
    沈念sama閱讀 33,783評(píng)論 4 324
  • 正文 年R本政府宣布缘揪,位于F島的核電站,受9級(jí)特大地震影響义桂,放射性物質(zhì)發(fā)生泄漏找筝。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評(píng)論 3 307
  • 文/蒙蒙 一慷吊、第九天 我趴在偏房一處隱蔽的房頂上張望袖裕。 院中可真熱鬧,春花似錦溉瓶、人聲如沸急鳄。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,333評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)攒岛。三九已至赖临,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間灾锯,已是汗流浹背兢榨。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,559評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留顺饮,地道東北人吵聪。 一個(gè)月前我還...
    沈念sama閱讀 45,595評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像兼雄,于是被迫代替她去往敵國(guó)和親吟逝。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評(píng)論 2 345

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