廣度優先搜索(BFS)算法

廣度優先搜索(BFS)算法

寬度優先搜索算法(又稱廣度優先搜索)是最簡便的圖的搜索算法之一,這一算法也是很多重要的圖的算法的原型。Dijkstra單源最短路徑算法和Prim最小生成樹算法都採用了和寬度優先搜索類似的思想。

已知圖G=(V,E)和一個源頂點s,寬度優先搜索以一種系統的方式探尋G的邊,從而“發現”s所能到達的所有頂點,並計算s到所有這些頂點的距離(最少邊數),該算法同時能生成一棵根爲s且包括所有可達頂點的寬度優先樹。對從s可達的任意頂點v,寬度優先樹中從s到v的路徑對應於圖G中從s到v的最短路徑,即包含最小邊數的路徑。該算法對有向圖和無向圖同樣適用。

之所以稱之爲寬度優先算法,是因爲算法自始至終一直通過已找到和末找到頂點之間的邊界向外擴展,就是說,算法首先搜索和s距離爲k的所有頂點,然後再去搜索和S距離爲k+l的其他頂點。

爲了保持搜索的軌跡,寬度優先搜索爲每個頂點着色:白色、灰色或黑色。算法開始前所有頂點都是白色,隨着搜索的進行,各頂點會逐漸變成灰色,然後成爲黑色。在搜索中第一次碰到一頂點時,我們說該頂點被發現,此時該頂點變爲非白色頂點。因此,灰色和黑色頂點都已被發現,但是,寬度優先搜索算法對它們加以區分以保證搜索以寬度優先的方式執行。若(u,v)∈E且頂點u爲黑色,那麼頂點v要麼是灰色,要麼是黑色,就是說,所有和黑色頂點鄰接的頂點都已被發現。灰色頂點可以與一些白色頂點相鄰接,它們代表着已找到和未找到頂點之間的邊界。

在寬度優先搜索過程中建立了一棵寬度優先樹,起始時只包含根節點,即源頂點s.在掃描已發現頂點u的鄰接表的過程中每發現一個白色頂點v,該頂點v及邊(u,v)就被添加到樹中。在寬度優先樹中,我們稱結點u是結點v的先輩或父母結點。因爲一個結點至多隻能被發現一次,因此它最多只能有--個父母結點。相對根結點來說祖先和後裔關係的定義和通常一樣:如果u處於樹中從根s到結點v的路徑中,那麼u稱爲v的祖先,v是u的後裔。

下面的寬度優先搜索過程BFS假定輸入圖G=(V,E)採用鄰接表表示,對於圖中的每個頂點還採用了幾種附加的數據結構,對每個頂點u∈V,其色彩存儲於變量color[u]中,結點u的父母存於變量π[u]中。如果u沒有父母(例如u=s或u還沒有被檢索到),則 π[u]=NIL,由算法算出的源點s和頂點u之間的距離存於變量d[u]中,算法中使用了一個先進先出隊列Q來存放灰色節點集合。其中head[Q]表示隊列Q的隊頭元素,Enqueue(Q,v)表示將元素v入隊, Dequeue(Q)表示對頭元素出隊;Adj[u]表示圖中和u相鄰的節點集合。

  procedure BFS(G,S);
   begin
1.   for 每個節點u∈V[G]-{s} do
        begin
2.        color[u]←White;
3.        d[u]←∞;
4.        π[u]←NIL;
        end;
5.   color[s]←Gray;
6.   d[s]←0;
7.   π[s]←NIL;
8.   Q←{s}
9.   while Q≠φ do
       begin
10.      u←head[Q];
11.      for 每個節點v∈Adj[u] do
12.        if color[v]=White then
              begin
13.             color[v]←Gray;        
14.             d[v]←d[v]+1;
15.             π[v]←u;
16.             Enqueue(Q,v);
              end;   
17.      Dequeue(Q);
18.      color[u]←Black;
       end;
   end; 

圖1展示了用BFS在例圖上的搜索過程。黑色邊是由BFS產生的樹枝。每個節點u內的值爲d[u],圖中所示的隊列Q是第9-18行while循環中每次迭代起始時的隊列。隊列中每個結點下面是該結點與源結點的距離。

圖1 BFS在一個無向圖上的執行過程

   過程BFS按如下方式執行,第1-4行置每個結點爲白色,置d[u]爲無窮大,每個結點的父母置爲NIL,第5行置源結點S爲灰色,即意味着過程開始時源結點已被發現。第6行初始化d[s]爲0,第7行置源結點的父母結點爲NIL,第8行初始化隊列0,使其僅含源結點s,以後Q隊列中僅包含灰色結點的集合。

程序的主循環在9-18行中,只要隊列Q中還有灰色結點,即那些已被發現但還沒有完全搜索其鄰接表的結點,循環將一直進行下去。第10行確定隊列頭的灰色結點爲u。第11-16行的循環考察u的鄰接表中的每一個頂點v。如果v是白色結點,那麼該結點還沒有被發現過,算法通過執行第13-16行發現該結點。首先它被置爲灰色,距離d[v]置爲d[u]+1,而後u被記爲該節點的父母,最後它被放在隊列Q的隊尾。當結點u的鄰接表中的所有結點都被檢索後,第17-18行使u彈出隊列並置成黑色。

分析

在證明寬度優先搜索的各種性質之前,我們先做一些相對簡單的工作——分析算法在圖G=(V,E)之上的運行時間。在初始化後,再沒有任何結點又被置爲白色。因此第12行的測試保證每個結點至多隻能迸人隊列一次,因而至多隻能彈出隊列一次。入隊和出隊操作需要O(1)的時間,因此隊列操作所佔用的全部時間爲O(V),因爲只有當每個頂點將被彈出隊列時纔會查找其鄰接表,因此每個頂點的鄰接表至多被掃描一次。因爲所有鄰接表的長度和爲Q(E),所以掃描所有鄰接表所花費時間至多爲O(E)。初始化操作的開銷爲O(V),因此過程BFS的全部運行時間爲O(V+E),由此可見,寬度優先搜索的運行時間是圖的鄰接表大小的一個線性函數。

最短路徑

在本部分的開始,我們講過,對於一個圖G=(V,E),寬度優先搜索算法可以得到從已知源結點s∈V到每個可達結點的距離,我們定義最短路徑長度δ(s,v)爲從頂點s到頂點v的路徑中具有最少邊數的路徑所包含的邊數,若從s到v沒有通路則爲∞。具有這一距離δ(s,v)的路徑即爲從s到v的最短路徑(後文我們將把最短路徑推廣到賦權圖,其中每邊都有一個實型的權值,一條路徑的權是組成該路徑所有邊的權值之和,目前討論的圖都不是賦權圖)。在證明寬度優先搜索計算出的就是最短路徑長度之前,我們先看一下最短路徑長度的一個重要性質。

引理1

設G=(V,E)是一個有向圖或無向圖,s∈V爲G的任意一個結點,則對任意邊(u,v)∈E,

δ(s,v)≤δ(s,u)+1

證明:

    如果從頂點s可達頂點u,則從s也可達v。在這種情況下從s到v的最短路徑不可能比從s到u的最短路徑加上邊(u,v)更長,因此不等式成立;如果從s不可達頂點u,則δ(s,v)=∞,不等式仍然成立。

我們試圖說明對每個頂點v∈V,BFS過程算出的d[v]=δ(s,v),下面我們首先證明d[v]是δ(s,v)的上界。

引理2

設G=(V,E)是一個有向或無向圖,並假設算法BFS從G中一已知源結點s∈V開始執行,在執行終止時,對每個頂點v∈V,變量d[v]的值滿足:d[v]≥δ(s,v)。

證明:

我們對一個頂點進入隊列Q的次數進行歸納,我們歸納前假設在所有頂點v∈V,d[v]≥δ(s,v)成立。

歸納的基礎是BFS過程第8行當結點s被放入隊列Q後的情形,這時歸納假設成立,因爲對於任意結點v∈V-{s},d[s]=0=δ(s,s)且d[v]=∞≥δ(s,v)。

然後進行歸納,考慮從頂點u開始的搜索中發現一白色頂點v,按歸納假設,d[u]≥δ(s,u)。從過程第14行的賦值語句以及引理1可知

d[v]=d[u]+1≥δ(s,u)+1≥δ(s,v)

然後,結點v被插入隊列Q中。它不會再次被插入隊列,因爲它已被置爲灰色,而第13-16行的then子句只對白色結點進行操作,這樣d[v]的值就不會改變,所以歸納假設成立。

爲了證明d[v]=δ(s,v),首先我們必須更精確地展示在BFS執行過程中是如何對隊列進行操作的,下面一個引理說明無論何時,隊列中的結點至多有兩個不同的d值。

引理3

假設過程BFS在圖G=(V,E)之上的執行過程中,隊列Q包含如下結點<v1,v2,...,vr>,其中v1是隊列Q的頭,vr是隊列的尾,則d[vi]≤d[v1]+1且d[vi]≤d[vi+1], i=1,2,..,r-1。

證明:

證明過程是對隊列操作的次數進行歸納。初始時,隊列僅包含頂點s,引理自然正確。

下面進行歸納,我們必須證明在壓入和彈出一個頂點後引理仍然成立。如果隊列的頭v1被彈出隊列,新的隊頭爲v2(如果此時隊列爲空,引理無疑成立),所以有d[vr]≤d[v1]+1≤d[v2]+1,餘下的不等式依然成立,因此v2爲隊頭時引理成立。要插入一個結點入隊列需仔細分析過程BFS,在BFS的第16行,當頂點v加入隊列成爲vr+1時,隊列頭v1實際上就是正在掃描其鄰接表的頂點u,因此有d[vr+1]=d[v]=d[u]+1=d[v1]+1,這時同樣有d[vr]≤d[v1]+1=d[u]+1=d[v]=d[vr+1],餘下的不等式d[vr]≤d[vr+1]仍然成立,因此當結點v插入隊列時引理同樣正確。

現在我們可以證明寬度優先搜索算法能夠正確地計算出最短路徑長度。

定理1 寬度優先搜索的正確性

設G=(V,E)是一個有向圖或無向圖,並假設過程BFS從G上某頂點s∈V開始執行,則在執行過程中,BFS可以發現源結點s可達的每一個結點v∈V,在運行終止時,對任意v∈V,d[v]=δ(s,v)。此外,對任意從s可達的節點v≠s,從s到v的最短路徑之一是從s到π[v]的最短路徑再加上邊(π[v],v)。

證明:

我們先證明結點v是從s不可達的情形。由引理2,d[v]≥δ(s,v)=∞,根據過程第14行,頂點v不可能有一個有限的d[v]值,由歸納可知,不可能有滿足下列條件的第一個頂點存在:該頂點的d值被過程的第14行語句置爲∞,因此僅對有有限d值的頂點,第14行語句纔會被執行。所以若v是不可達的話,它將不會在搜索中被發現。

證明主要是對由s可達的頂點來說的。設Vk表示和s距離爲k的頂點集合,即Vk={v∈V:δ(s,v)=k}。證明過程爲對k進行歸納。作爲歸納假設,我們假定對於每一個頂點v∈Vk,在BFS的執行中只有某一特定時刻滿足:

  • 結點v爲灰色;
  • d[v]被置爲k;
  • 如果v≠s,則對於某個u∈Vk-1,π[v]被置爲u;
  • v被插入隊列Q中;

正如我們先前所述,至多隻有一個特定時刻滿足上述條件。

歸納的初始情形爲k=0,此時V0={s},因爲顯然源結點s是唯一和s距離爲0的結點,在初始化過程中,s被置爲灰色,d[s]被置爲0,且s被放人隊列Q中,所以歸納假設成立。

下面進行歸納,我們須注意除非到算法終止,隊列Q不爲空,而且一旦某結點u被插入隊列,d[u]和π[u]都不再改變。根據引理3可知如果在算法過程中結點按次序v1,v2,...,vr被插入隊列,那麼相應的距離序列是單調遞增的:d[vi]≤d[vi+1],i=1,2,...,r-1。

現在我們考慮任意結點v∈Vk,k≥1。根據單調性和d[v]≥k(由引理2)和歸納假設,可知如果v能夠被發現,則必在Vk-1中的所有結點進入隊列之後。

由δ(s,v)=k,可知從s到v有一條具有k邊的通路,因此必存在某結點u∈Vk-1,且(u,v)∈E。不失一般性,設u是滿足條件的第一個灰色節點(根據歸納可知集合Vk-1中的所有結點都被置爲灰色),BFS把每一個灰色結點放入隊列中,這樣由第10行可知結點u最終必然會作爲隊頭出現。當已成爲隊頭時,它的鄰接表將被掃描就會發現結點v(結點v不可能在此之前被發現,因爲它不與Vj(j<k-1)中的任何結點相鄰接,否則v不可能屬於Vk,並且根據假定,u是和v相鄰接的Vk-1中被發現的第一個結點)。第13行置v爲灰色,第14行置d[v]=d[u]+l=k,第15行置π[v]爲u,第16行把v插入隊列中。由於v是Vk中的任意結點,因此證明歸納假設成立。

在結束定理的證明前,我們注意到如果v∈Vk,則據我們所知可得π[v]∈Vk-1,這樣我們就得到了一條從s到v的最短路徑:即爲從s到π[v]的最短路徑再通過邊(π[v],v)。

寬度優先樹

過程BFS在搜索圖的同時建立了一棵寬度優先樹,如圖1所示,這棵樹是由每個結點的π域所表示。我們正式定義先輩子圖如下,對於圖G=(V,E),源頂點爲s,其先輩子圖Gπ=(Vπ,Eπ)滿足:

Vπ={v∈V:π[v]≠NIL}∪{s}

Eπ={(π[v],v)∈E:v∈Vπ-{s}}

如果Vπ由從s可達的頂點構成,那麼先輩子圖Gπ是一棵寬度優先樹,並且對於所有v∈Vπ,Gπ中唯一的由s到v的簡單路徑也同樣是G中從s到v的一條最短路徑。由於它互相連通,且|Eπ|=|Vπ|-1(由樹的性質),所以寬度優先樹事實上就是一棵樹,Eπ中的邊稱爲樹枝。

當BFS從圖G的源結點s開始執行後,下面的引理說明先輩子圖是一棵寬度優先樹。

引理4

當過程BFS應用於某一有向或無向圖G=(V,E)時,該過程同時建立的π域滿足條件:其先輩子圖Gπ=(Vπ,Eπ)是一棵寬度優先樹。

證明:

過程BFS的第15行語句對(u,v)∈E且δ(s,v)<∞(即v從s可達)置π[v]=u,因此Vπ是由V中從v可達的頂點所組成,由於Gπ形成一棵樹,所以它包含從s到Vπ中每一結點的唯一路徑,由定理1進行歸納,我們可知其每條路徑都是一條最短路徑。(證畢)

下面的過程將打印出從S到v的最短路徑上的所有結點,假定已經運行完BFS並得出了最短路徑樹。

 procedure Print_Path(G,s,v);
  begin
1.  if v=s 
2.     then write(s)
3.     else if π[v]=nil 
4.             then writeln('no path from ',s,' to ',v, 'exists.')
               else 
                 begin
5.                 Print_Path(G,s,π[v]);
6.                 write(v);
                 end; 
  end;

因爲每次遞歸調用的路徑都比前一次調用少一個頂點,所以該過程的運行時間是關於打印路徑上頂點數的一個線性函數。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章