日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

算法:深度优先遍历和广度优先遍历

發(fā)布時(shí)間:2023/12/31 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 算法:深度优先遍历和广度优先遍历 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

什么是深度、廣度優(yōu)先遍歷

圖的遍歷是指,從給定圖中任意指定的頂點(diǎn)(稱為初始點(diǎn))出發(fā),按照某種搜索方法沿著圖的邊訪問圖中的所有頂點(diǎn),使每個(gè)頂點(diǎn)僅被訪問一次,這個(gè)過程稱為圖的遍歷。遍歷過程中得到的頂點(diǎn)序列稱為圖遍歷序列。

圖的遍歷過程中,根據(jù)搜索方法的不同,又可以劃分為兩種搜索策略:

  • 深度優(yōu)先搜索(DFS,Depth First Search)
  • 廣度優(yōu)先搜索(BFS,Breadth First Search)

實(shí)現(xiàn)深度優(yōu)先遍歷的關(guān)鍵在于回溯,實(shí)現(xiàn)廣度優(yōu)先遍歷的關(guān)鍵在于回放。

深度優(yōu)先搜索

深度優(yōu)先搜索(Depth-First-Search),簡稱 DFS。最直觀的例子就是“走迷宮”。假設(shè)你站在迷宮的某個(gè)岔路口,然后想找到出口。你隨意選擇一個(gè)岔路口來走,走著走著發(fā)現(xiàn)走不通的時(shí)候,你就回退到上一個(gè)岔路口,重新選擇一條路繼續(xù)走,直到最終找到出口。這種走法就是一種深度優(yōu)先搜索策略。

那么,如何在圖中應(yīng)用深度優(yōu)先搜索呢?

算法思想

  • 對于圖來說:

    • 假設(shè)初始狀態(tài)是圖中所有頂點(diǎn)均未被訪問
    • 從某個(gè)頂點(diǎn)出發(fā),然后依次從它的各個(gè)未被訪問的鄰接點(diǎn)出發(fā)深度優(yōu)先搜索遍歷圖,直至圖中所有和v有路徑相通的頂點(diǎn)都被訪問到。
    • 若此時(shí)尚有其他頂點(diǎn)未被訪問到,則另選一個(gè)未被訪問的頂點(diǎn)作起始點(diǎn),重復(fù)上述過程,直至圖中所有頂點(diǎn)都被訪問到為止。
  • 實(shí)現(xiàn)深度優(yōu)先遍歷的關(guān)鍵在于回溯。所謂“回溯”,就是自后往前,追溯曾經(jīng)走過的路徑。

算法特點(diǎn)

  • 深度優(yōu)先搜索是一個(gè)遞歸的過程。

    • 首先,選定一個(gè)出發(fā)點(diǎn)后進(jìn)行遍歷,如果有鄰接的未被訪問過的節(jié)點(diǎn)則繼續(xù)前進(jìn)。
    • 若不能繼續(xù)前進(jìn),則回退一步再前進(jìn)
    • 若回退一步仍然不能前進(jìn),則連續(xù)回退至可以前進(jìn)的位置為止。
    • 重復(fù)此過程,直到所有與選定點(diǎn)相通的所有頂點(diǎn)都被遍歷。
  • 深度優(yōu)先搜索是遞歸過程,帶有回退操作,因此需要使用棧存儲(chǔ)訪問的路徑信息。當(dāng)訪問到的當(dāng)前頂點(diǎn)沒有可以前進(jìn)的鄰接頂點(diǎn)時(shí),需要進(jìn)行出棧操作,將當(dāng)前位置回退至出棧元素位置。

圖解過程

無向圖的深度優(yōu)先遍歷

以下圖所示無向圖說明深度優(yōu)先搜索遍歷過程。

實(shí)例一


假設(shè)我們從頂點(diǎn)A開始,遍歷過程中的每一步如下:

  • 首先選取頂點(diǎn)A為起始點(diǎn),輸出A頂點(diǎn)信息,而且將A入棧,并且標(biāo)記A為已訪問頂點(diǎn)
  • A的鄰接頂點(diǎn)有C、D、F,從中任意選取一個(gè)頂點(diǎn)前進(jìn)。這里我們選取C為前進(jìn)位置頂點(diǎn)。輸出C頂點(diǎn)信息,將C入棧,并標(biāo)記C為已訪問頂點(diǎn)。當(dāng)前位置指向頂點(diǎn)C
  • 頂點(diǎn)C的鄰接頂點(diǎn)有A、D、B,此時(shí)A已經(jīng)標(biāo)記為已訪問頂點(diǎn),因此不能繼續(xù)訪問。從B或者D中選取一個(gè)頂點(diǎn)前進(jìn),這里我們選取B頂點(diǎn)為前進(jìn)位置頂點(diǎn)。輸出B頂點(diǎn)信息,將B入棧,標(biāo)記B頂點(diǎn)為已訪問頂點(diǎn)。當(dāng)前位置指向B
  • 頂點(diǎn)B的鄰接頂點(diǎn)只有C、E,C已被標(biāo)記,不能繼續(xù)訪問,因此選取E為前進(jìn)位置頂點(diǎn),輸出E頂點(diǎn)信息,將E入棧,標(biāo)記E頂點(diǎn),當(dāng)前位置指向E。
  • 頂點(diǎn)E的鄰接頂點(diǎn)均已被標(biāo)記,此時(shí)無法繼續(xù)前進(jìn),則需要進(jìn)行回退。將當(dāng)前位置回退至頂點(diǎn)B,回退的同時(shí)將E出棧。
  • 頂點(diǎn)B的鄰接頂點(diǎn)也均被標(biāo)記,需要繼續(xù)回退,當(dāng)前位置回退至C,回退同時(shí)將B出棧。
  • 頂點(diǎn)C可以前進(jìn)的頂點(diǎn)位置為D,則輸出D頂點(diǎn)信息,將D入棧,并標(biāo)記D頂點(diǎn)。當(dāng)前位置指向頂點(diǎn)D。
  • 頂點(diǎn)D沒有前進(jìn)的頂點(diǎn)位置,因此需要回退操作。將當(dāng)前位置回退至頂點(diǎn)C,回退同時(shí)將D出棧。
  • 頂點(diǎn)C沒有前進(jìn)的頂點(diǎn)位置,繼續(xù)回退,將當(dāng)前位置回退至頂點(diǎn)A,回退同時(shí)將C出棧。
  • 頂點(diǎn)A前進(jìn)的頂點(diǎn)位置為F,輸出F頂點(diǎn)信息,將F入棧,并標(biāo)記F。將當(dāng)前位置指向頂點(diǎn)F。
  • 頂點(diǎn)F的前進(jìn)頂點(diǎn)位置為G,輸出G頂點(diǎn)信息,將G入棧,并標(biāo)記G。將當(dāng)前位置指向頂點(diǎn)G。
  • 頂點(diǎn)G沒有前進(jìn)頂點(diǎn)位置,回退至F。當(dāng)前位置指向F,回退同時(shí)將G出棧。
  • 頂點(diǎn)F沒有前進(jìn)頂點(diǎn)位置,回退至A,當(dāng)前位置指向A,回退同時(shí)將F出棧。
  • 頂點(diǎn)A沒有前進(jìn)頂點(diǎn)位置,繼續(xù)回退,棧為空,則以A為起始的遍歷結(jié)束。若圖中仍有未被訪問的頂點(diǎn),則選取未訪問的頂點(diǎn)為起始點(diǎn),繼續(xù)執(zhí)行此過程。直至所有頂點(diǎn)均被訪問。
  • 采用深度優(yōu)先搜索遍歷順序?yàn)?font color="red">A->C->B->E->D->F->G。

利用一個(gè)臨時(shí)棧來實(shí)現(xiàn)回溯,最終遍歷完所有頂點(diǎn)

問題:

(1)必須選取A作為遍歷的起點(diǎn)嗎?

  • 不是原則我們可以選取任何一個(gè)節(jié)點(diǎn)作為起點(diǎn)進(jìn)行開始,進(jìn)行深度優(yōu)先遍歷

(2)當(dāng)有多個(gè)鄰接點(diǎn)未被訪問時(shí),可以選取哪個(gè)作為下一個(gè)起點(diǎn)呢?

  • 隨便哪個(gè)都行。
  • 當(dāng)有多個(gè)臨界點(diǎn)可選時(shí),相當(dāng)于走迷宮時(shí)出現(xiàn)了多個(gè)分叉路口,我們只要不走之前走過的路就行了。所以關(guān)鍵在于標(biāo)記哪個(gè)點(diǎn)是否已經(jīng)走過。不過,一般我們會(huì)定義一個(gè)原則,必須不碰重復(fù)點(diǎn)的情況下,選擇走左/右手第一條沒有走過的路,這樣比較好理解

兩個(gè)原則:

  • 右手原則: 在沒有碰到重復(fù)頂點(diǎn)的情況下,分叉路口始終是向右手邊走,每路過一個(gè)頂點(diǎn)就做一個(gè)記號
  • 左手原則: 在沒有碰到重復(fù)頂點(diǎn)的情況下,分叉路口始終是向左手邊走,每路過一個(gè)頂點(diǎn)就做一個(gè)記號

下面以右手原則進(jìn)行深度優(yōu)先遍歷再看個(gè)例子

實(shí)例二

原則我們可以選取任何一個(gè)節(jié)點(diǎn)作為起點(diǎn)進(jìn)行開始,進(jìn)行深度優(yōu)先遍歷,假設(shè)我們從頂點(diǎn)A開始,遍歷過程中的每一步如下:

  • 第一步:從頂點(diǎn)A開始,將頂點(diǎn)A標(biāo)記為已訪問節(jié)點(diǎn)

  • 第二步:根據(jù)右手原則,訪問頂點(diǎn)B,并將B標(biāo)記為已訪問節(jié)點(diǎn)

  • 第三步:右手原則,訪問頂點(diǎn)C

  • 第四步:右手原則,訪問頂點(diǎn)D

  • 第五步:右手原則,訪問頂點(diǎn)E

  • 第六步:右手原則,訪問頂點(diǎn)F

  • 第七步:右手原則,應(yīng)該先訪問頂點(diǎn)F的鄰接頂點(diǎn)A,但發(fā)現(xiàn)A已經(jīng)被訪問,則訪問A之外的最右側(cè)頂點(diǎn)G

  • 第八步:右手原則,先訪問頂點(diǎn)B,頂點(diǎn)B已經(jīng)被訪問;在訪問頂點(diǎn)D,頂點(diǎn)D已經(jīng)被訪問;最后訪問頂點(diǎn)H

  • 第九步:發(fā)現(xiàn)頂點(diǎn)H的鄰接頂點(diǎn)均已被訪問,則退回到頂點(diǎn)G;

  • 第十步:頂點(diǎn)G的鄰接頂點(diǎn)均已被訪問,則退回到頂點(diǎn)F;

  • 第十一步:頂點(diǎn)F的鄰接頂點(diǎn)已被訪問,則退回到頂點(diǎn)E;

  • 第十二步:頂點(diǎn)E的鄰接頂點(diǎn)均已被訪問,則退回到頂點(diǎn)D;

  • 第十三步:頂點(diǎn)D的鄰接頂點(diǎn)I尚未被訪問,則訪問頂點(diǎn)I;

  • 第十四步:頂點(diǎn)I的鄰接頂點(diǎn)均已被訪問,則退回到頂點(diǎn)D;

  • 第十五步:頂點(diǎn)D的鄰接頂點(diǎn)均已被訪問,退回到頂點(diǎn)C;

  • 第十六步:頂點(diǎn)C的鄰接頂點(diǎn)均已被訪問,則退回到頂點(diǎn)B;

  • 頂點(diǎn)B的鄰接頂點(diǎn)均已被訪問,則退回到頂點(diǎn)A,頂點(diǎn)A為起始頂點(diǎn),深度優(yōu)先搜索結(jié)束。

圖的深度優(yōu)先搜索和二叉樹的前序遍歷、中序遍歷、后序遍歷本質(zhì)上均屬于一類方法。

上面的過程可以總結(jié)為以下3個(gè)步驟:

  • 首先選定一個(gè)未被訪問過的頂點(diǎn)V作為起始頂點(diǎn)(或者訪問指定的起始頂點(diǎn)V),并將其標(biāo)記為已訪問

  • 然后搜索與頂點(diǎn)V鄰接的所有頂點(diǎn),判斷這些頂點(diǎn)是否被訪問過,如果有未被訪問過的頂點(diǎn)W;再選取與頂點(diǎn)W鄰接的未被訪問過的一個(gè)頂點(diǎn)并進(jìn)行訪問,依次重復(fù)進(jìn)行。當(dāng)一個(gè)頂點(diǎn)的所有的鄰接頂點(diǎn)都被訪問過時(shí),則依次回退到最近被訪問的頂點(diǎn)。若該頂點(diǎn)還有其他鄰接頂點(diǎn)未被訪問,則從這些未被訪問的頂點(diǎn)中取出一個(gè)并重復(fù)上述過程,直到與起始頂點(diǎn)V相鄰接的所有頂點(diǎn)都被訪問過為止。

  • 若此時(shí)圖中依然有頂點(diǎn)未被訪問,則再選取其中一個(gè)頂點(diǎn)作為起始頂點(diǎn)并進(jìn)行遍歷,轉(zhuǎn)(2)。反之,則遍歷結(jié)束。

  • 有向圖深度優(yōu)先搜索


    (1)以頂點(diǎn)A為起始點(diǎn),輸出A,將A入棧,并標(biāo)記A為已經(jīng)訪問。當(dāng)前位置指向A。

    (2)以A為尾的邊只有1條,且邊的頭為頂點(diǎn)B,則前進(jìn)位置為頂點(diǎn)B,輸出B,將B入棧,標(biāo)記B。當(dāng)前位置指向B。

    (3)頂點(diǎn)B可以前進(jìn)的位置有C與F,選取F為前進(jìn)位置,輸出F,將F入棧,并標(biāo)記F。當(dāng)前位置指向F。

    (4)頂點(diǎn)F的前進(jìn)位置為G,輸出G,將G入棧,并標(biāo)記G。當(dāng)前位置指向G。

    (5)頂點(diǎn)G沒有可以前進(jìn)的位置,則回退至F,將G出棧。當(dāng)前位置指向F。

    (6)頂點(diǎn)F沒有可以前進(jìn)的位置,繼續(xù)回退至B,將F出棧。當(dāng)前位置指向B。

    (7)頂點(diǎn)B可以前進(jìn)位置為C和E,選取E,輸出E,將E入棧,并標(biāo)記E。當(dāng)前位置指向E。

    (8)頂點(diǎn)E的前進(jìn)位置為D,輸出D,將D入棧,并標(biāo)記D。當(dāng)前位置指向D。

    (9)頂點(diǎn)D的前進(jìn)位置為C,輸出C,將C入棧,并標(biāo)記C。當(dāng)前位置指向C。

    (10)頂點(diǎn)C沒有前進(jìn)位置,進(jìn)行回退至D,回退同時(shí)將C出棧。

    (11)繼續(xù)執(zhí)行此過程,直至棧為空,以A為起始點(diǎn)的遍歷過程結(jié)束。若圖中仍有未被訪問的頂點(diǎn),則選取未訪問的頂點(diǎn)為起始點(diǎn),繼續(xù)執(zhí)行此過程。直至所有頂點(diǎn)均被訪問。

    性能分析

    當(dāng)圖采用鄰接矩陣存儲(chǔ)時(shí),由于矩陣元素個(gè)數(shù)為n2n^2n2,因此時(shí)間復(fù)雜度就是O(n2)O(n^2)O(n2)

    當(dāng)圖采用鄰接表存儲(chǔ)時(shí),鄰接表中只是存儲(chǔ)了邊結(jié)點(diǎn)(e條邊,無向圖也只是2e個(gè)結(jié)點(diǎn)),加上表頭結(jié)點(diǎn)為n(也就是頂點(diǎn)個(gè)數(shù)),因此時(shí)間復(fù)雜度為O(n+e)。

    廣度優(yōu)先搜索

    算法思想

    • 思想:
      • 從圖中某頂點(diǎn)v出發(fā),在訪問了v之后依次訪問v的各個(gè)未曾訪問過的鄰接點(diǎn)
      • 然后分別從這些鄰接點(diǎn)出發(fā)依次訪問它們的鄰接點(diǎn),并使得“先被訪問的頂點(diǎn)的鄰接點(diǎn)先于后被訪問的頂點(diǎn)的鄰接點(diǎn)被訪問,直至圖中所有已被訪問的頂點(diǎn)的鄰接點(diǎn)都被訪問到。
      • 如果此時(shí)圖中尚有頂點(diǎn)未被訪問,則需要另選一個(gè)未曾被訪問過的頂點(diǎn)作為新的起始點(diǎn),重復(fù)上述過程,直至圖中所有頂點(diǎn)都被訪問到為止。
    • 實(shí)現(xiàn)廣度優(yōu)先遍歷的關(guān)鍵在于回放

    回溯與重放是完全相反的過程。

    仍然以剛才的圖為例,按照廣度優(yōu)先遍歷的思想

    • 我們先遍歷頂點(diǎn)0,然后遍歷其鄰接點(diǎn)1、3
    • 接下來我們要遍歷更外圍的頂點(diǎn),可是如何找到這些更外圍的頂點(diǎn)呢?我們需要把剛才遍歷過的頂點(diǎn)1,3按照順序回顧一遍,從頂點(diǎn)1發(fā)現(xiàn)了鄰接點(diǎn)2,從頂點(diǎn)3發(fā)現(xiàn)了鄰接點(diǎn)4。于是得到了順序2,4
    • 再把剛才遍歷過的頂點(diǎn)2,4按照順序回顧一遍,分別得到鄰接點(diǎn)5,6
    • 再把剛才遍歷過的頂點(diǎn)5,7按照順序回顧一遍,分別得到鄰接點(diǎn)7,7。7只需要打印一次,所以我們需要一個(gè)東西來標(biāo)記當(dāng)前頂點(diǎn)是否已經(jīng)訪問過

    像這樣把遍歷過的頂點(diǎn)按照之前的遍歷順序重新回顧,就叫做重放

    • 同樣的,要實(shí)現(xiàn)重放也需要額外的存儲(chǔ)空間,可以利用隊(duì)列的先入先出特性來實(shí)現(xiàn)。
    • 另外,還需要標(biāo)記某個(gè)點(diǎn)是否已經(jīng)被訪問過,可以用數(shù)組、set等來實(shí)現(xiàn)

    可以看出,廣度優(yōu)先搜索它其實(shí)就是一種“地毯式”層層推進(jìn)的搜索策略,即先查找離起始頂點(diǎn)最近的,然后是次近的,依次往外搜索。

    算法特點(diǎn)

    廣度優(yōu)先搜索類似于樹的層次遍歷,是按照一種由近及遠(yuǎn)的方式訪問圖的頂點(diǎn)。在進(jìn)行廣度優(yōu)先搜索時(shí)需要使用隊(duì)列存儲(chǔ)頂點(diǎn)信息。

    圖解過程

    無向圖的廣度優(yōu)先搜索

    (1)選取A為起始點(diǎn),輸出A,A入隊(duì)列,標(biāo)記A,當(dāng)前位置指向A。

    (2)隊(duì)列頭為A,A出隊(duì)列。A的鄰接頂點(diǎn)有B、E,輸出B和E,并將B和E入隊(duì),以及標(biāo)記B、E為已訪問。當(dāng)前位置指向B。

    (3)隊(duì)列頭為B,B出隊(duì)列。B的鄰接頂點(diǎn)有C、D,輸出C、D,將C、D入隊(duì)列,并標(biāo)記C、D。當(dāng)前位置指向B。

    (4)隊(duì)列頭為E,E出隊(duì)列。E的鄰接頂點(diǎn)有D、F,但是D已經(jīng)被標(biāo)記,因此輸出F,將F入隊(duì)列,并標(biāo)記F。當(dāng)前位置指向E。


    (5)隊(duì)列頭為C,C出隊(duì)列。C的鄰接頂點(diǎn)有B、D,但B、D均被標(biāo)記。無元素入隊(duì)列。當(dāng)前位置指向C。


    (6)隊(duì)列頭為D,D出隊(duì)列。D的鄰接頂點(diǎn)有B、C、E,但是B、C、E均被標(biāo)記,無元素入隊(duì)列。當(dāng)前位置指向D。

    (7)隊(duì)列頭為F,F出隊(duì)列。F的鄰接頂點(diǎn)有G、H,輸出G、H,將G、H入隊(duì)列,并標(biāo)記G、H。當(dāng)前位置指向F。

    (8)隊(duì)列頭為G,G出隊(duì)列。G的鄰接頂點(diǎn)有F,但F已被標(biāo)記,無元素入隊(duì)列。當(dāng)前位置指向G。

    (9)隊(duì)列頭為H,H出隊(duì)列。H的鄰接頂點(diǎn)有F,但F已被標(biāo)記,無元素入隊(duì)列。當(dāng)前位置指向H。

    (10)隊(duì)列空,則以A為起始點(diǎn)的遍歷結(jié)束。若圖中仍有未被訪問的頂點(diǎn),則選取未訪問的頂點(diǎn)為起始點(diǎn),繼續(xù)執(zhí)行此過程。直至所有頂點(diǎn)均被訪問。

    有向圖的廣度優(yōu)先搜索


    (1)選取A為起始點(diǎn),輸出A,將A入隊(duì)列,標(biāo)記A。


    (2)隊(duì)列頭為A,A出隊(duì)列。以A為尾的邊有兩條,對應(yīng)的頭分別為B、C,則A的鄰接頂點(diǎn)有B、C。輸出B、C,將B、C入隊(duì)列,并標(biāo)記B、C。

    (3)隊(duì)列頭為B,B出隊(duì)列。B的鄰接頂點(diǎn)為C,C已經(jīng)被標(biāo)記,因此無新元素入隊(duì)列。


    (4)隊(duì)列頭為C,C出隊(duì)列。C的鄰接頂點(diǎn)有E、F。輸出E、F,將E、F入隊(duì)列,并標(biāo)記E、F。


    (5)列頭為E,E出隊(duì)列。E的鄰接頂點(diǎn)有G、H。輸出G、H,將G、H入隊(duì)列,并標(biāo)記G、H。


    (6)隊(duì)列頭為F,F出隊(duì)列。F無鄰接頂點(diǎn)

    (7)隊(duì)列頭為G,G出隊(duì)列。G無鄰接頂點(diǎn)


    (8)隊(duì)列頭為H,H出隊(duì)列。H鄰接頂點(diǎn)為E,但是E已被標(biāo)記,無新元素入隊(duì)列


    (9)隊(duì)列為空,以A為起始點(diǎn)的遍歷過程結(jié)束,此時(shí)圖中仍有D未被訪問,則以D為起始點(diǎn)繼續(xù)遍歷。選取D為起始點(diǎn),輸出D,將D入隊(duì)列,標(biāo)記D


    (10)隊(duì)列頭為D,D出隊(duì)列,D的鄰接頂點(diǎn)為B,B已被標(biāo)記,無新元素入隊(duì)列


    (11)隊(duì)列為空,且所有元素均被訪問,廣度優(yōu)先搜索遍歷過程結(jié)束。廣度優(yōu)先搜索的輸出序列為:A->B–>C->E->F->G->H->D。

    算法分析

    我們來看下,廣度優(yōu)先搜索的時(shí)間、空間復(fù)雜度是多少呢?假設(shè)圖有V個(gè)頂點(diǎn),E條邊

    • 每個(gè)頂點(diǎn)都需要進(jìn)出一遍隊(duì)列,每個(gè)邊都會(huì)被訪問一次。所以,廣度優(yōu)先搜索的時(shí)間復(fù)雜度是O(V+E)。當(dāng)然,對于一個(gè)連通圖來說,也就是說一個(gè)圖中的所有頂點(diǎn)都是連通的,,E 肯定要大于等于 V-1,所以,廣度優(yōu)先搜索的時(shí)間復(fù)雜度也可以簡寫為 O(E)
    • 廣度優(yōu)先搜索的空間消耗主要在幾個(gè)輔助變量 visited 數(shù)組、queue 隊(duì)列上。這兩個(gè)存儲(chǔ)空間的大小都不會(huì)超過頂點(diǎn)的個(gè)數(shù),所以空間復(fù)雜度是 O(V)。

    總結(jié)

    圖的遍歷主要就是這兩種遍歷思想,深度優(yōu)先搜索使用遞歸方式,需要棧結(jié)構(gòu)輔助實(shí)現(xiàn)。廣度優(yōu)先搜索需要使用隊(duì)列結(jié)構(gòu)輔助實(shí)現(xiàn)。在遍歷過程中可以看出,

    • 對于連通圖,從圖的任意一個(gè)頂點(diǎn)開始深度或廣度優(yōu)先遍歷一定可以訪問圖中的所有頂點(diǎn)
    • 對于非連通圖,從圖的任意一個(gè)頂點(diǎn)開始深度或廣度優(yōu)先遍歷并不能訪問圖中的所有頂點(diǎn)。

    實(shí)現(xiàn)

    深度優(yōu)先遍歷

    當(dāng)圖采用鄰接矩陣進(jìn)行存儲(chǔ),遞歸的實(shí)現(xiàn)操作:

    #define MAXVBA 100 #define INFINITY 65536typedef struct {char vexs[MAXVBA];int arc[MAXVBA][MAXVBA];int numVertexes, numEdges; } MGraph;// 鄰接矩陣的深度有限遞歸算法#define TRUE 1 #define FALSE 0 #define MAX 256typedef int Boolean; // 這里我們定義Boolean為布爾類型,其值為TRUE或FALSE Boolean visited[MAX]; // 訪問標(biāo)志的數(shù)組void DFS(MGraph G, int i){visited[i] = TRUE;printf("%c", G.vexs[i]);for (int j = 0; j < G.numVertexes; ++j) {if (G.arc[i][j] == 1 && !visited[j]){DFS(G, j); // 對為訪問的鄰接頂點(diǎn)遞歸調(diào)用}} }// 鄰接矩陣的深度遍歷操作 void DFSTraverse(MGraph G){int i;// 初始化所有頂點(diǎn)狀態(tài)都是未訪問過狀態(tài)for (i = 0; i < G.numVertexes; ++i) {visited[i] = FALSE;}//防止圖為非聯(lián)通的情況,遍歷整個(gè)圖for (i = 0; i < G.numVertexes; ++i) {if (!visited[i]){ // 若是連通圖,只會(huì)執(zhí)行一次DFS(G, i);}} }

    當(dāng)圖采用鄰接矩陣進(jìn)行存儲(chǔ),棧的實(shí)現(xiàn)操作:

    void DFS_Stack(MGraph G, int i) {int node;int count = 1;printf("%c ", G.vexs[i]); // 打印已訪問頂點(diǎn)visited[i] = TRUE;node = i;push(i); //開始的節(jié)點(diǎn)入棧while(count < G.numVertexes) //still has node not visited{/* 所有被訪問的節(jié)點(diǎn)依次入棧,只有node當(dāng)找不到下一個(gè)相連的節(jié)點(diǎn)時(shí),才使用出棧節(jié)點(diǎn) */for(j=0; j < G.numVertexes; j++){if(G.arc[node][j] == 1 && visited[j] == FALSE){visited[j] = TRUE;printf("%c ", G.vexs[j]);count++;push(j); //push node jbreak;}}if(j == G.numVertexes) //與node相連的頂點(diǎn)均已被訪問過,所以需要從stack里取出node的上一個(gè)頂點(diǎn),再看該頂點(diǎn)的鄰接頂點(diǎn)是否未被訪問node = pop();else //找到與node相連并且未被訪問的頂點(diǎn),node = j;} }


    鄰接表存儲(chǔ)下圖的深度優(yōu)先搜索代碼實(shí)現(xiàn),與鄰接矩陣的思想相同,只是實(shí)現(xiàn)略有不同:

    // 鄰接表的深度有限遞歸算法#define TRUE 1 #define FALSE 0 #define MAX 256typedef int Boolean; // 這里我們定義Boolean為布爾類型,其值為TRUE或FALSE Boolean visited[MAX]; // 訪問標(biāo)志的數(shù)組void DFS(GraphAdjList GL, int i) {EdgeNode *p;visited[i] = TRUE;printf("%c " GL->adjList[i].data);p = GL->adjList[i].firstEdge;while(p){if( !visited[p->adjvex] ){DFS(GL, p->adjvex);}p = p->next;} }// 鄰接表的深度遍歷操作 void DFSTraverse(GraphAdjList GL) {int i;for( i=0; i < GL->numVertexes; i++ ){visited[i] = FALSE; // 初始化所有頂點(diǎn)狀態(tài)都是未訪問過狀態(tài)}for( i=0; i < GL->numVertexes; i++ ){if( !visited[i] ) // 若是連通圖,只會(huì)執(zhí)行一次{DFS(GL, i);}} }

    廣度優(yōu)先遍歷

    // 鄰接矩陣的廣度遍歷算法 void BFSTraverse(MGraph G) {int i, j;Queue Q;for( i=0; i < G.numVertexes; i++ ){visited[i] = FALSE;}initQueue( &Q );for( i=0; i < G.numVertexes; i++ ){if( !visited[i] ){printf("%c ", G.vex[i]);visited[i] = TRUE;EnQueue(&Q, i);while( !QueueEmpty(Q) ){DeQueue(&Q, &i);for( j=0; j < G.numVertexes; j++ ){if( G.art[i][j]==1 && !visited[j] ){printf("%c ", G.vex[j]);visited[j] = TRUE;EnQueue(&Q, j);}}}}} }

    總結(jié)

    以上是生活随笔為你收集整理的算法:深度优先遍历和广度优先遍历的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。