廣度優先搜尋(BFS)

2021-09-11 18:53:24 字數 3902 閱讀 3169

廣度優先搜尋,英文名字為breadth-first search,又被稱為是寬度優先搜尋。它與深度優先搜尋相同的是,它也是從某乙個狀態出發可以到達所有的狀態。

也深度優先搜尋不同的是,它們的搜尋順序存在差異。廣度優先搜尋,顧名思義,它以廣度為優先搜尋的物件。它總是去搜尋距離初始狀態近的狀態(而不是像深度優先搜尋那樣一路走到黑,再退回來)。換句話說,它是按照開始狀態——>只需要一次轉移就可以到達的狀態——>只需要兩次就可以到達的狀態——>……這樣的順序進行搜尋。對於同一種狀態,寬度優先搜尋只經過一次,因此複雜度為 o(狀

態數×轉

移的方式

)o(狀態數 \times 轉移的方式)

o(狀態數×

轉移的方

式)

首先我們先要介紹一下佇列的有關知識,來繼續我們的學習。

關於佇列是什麼,可以看這個鏈結

戳這裡

然後我們還需要知道如何實現乙個佇列。在這裡提供兩種方法。一種用陣列模擬,一種用c++std中的queue。

#include

int l,r;

int que[n]

;//用來儲存資料的乙個佇列

/*要注意,只有在[l,r]之間的數字,才是佇列中的元素,其中l是隊首,r是隊尾。

*/void

push

(int x)

void

pop(

)int

front()

bool

empty()

intmain()

//如上所示即可實現乙個簡單的佇列

#include

#include

using

namespace std;

queue<

int>que;

//宣告乙個int型別的佇列,名字叫做que

intmain()

一般來說,廣搜的性質,決定了,它適用於那種求最短路或者最少運算元等的問題,適用於狀態數量少,並且可以有效判重的問題。在廣搜的問題上,一種狀態只會被訪問一次。為了避免狀態的重複訪問,所以我們需要判重。

深度優先(隱式地)利用了棧進行計算,而寬度優先搜尋則利用了佇列。

搜尋時先將初始狀態加入到佇列中,此後從佇列的最前端不斷取出狀態,把從該狀態可以轉移到的狀態中尚未訪問過的部分加入佇列,如此往復,直到佇列被取空或者已經找到了問題的解。通過觀察這個佇列我們就知道所有狀態都是按照距離初始狀態由近及遠的順序遍歷的。

廣搜很重要的乙個東西就是狀態的把握(其實不只是廣搜,深搜也一樣)。

拿迷宮問題來舉例。

如果我們要求的是最短路徑的長度。那麼在我們的狀態中,就需要記錄每乙個狀態第一次被訪問到的時候的已經走過的步數。(由廣搜由近及遠的搜尋順序,我們不難證明,這個步數就是的最短路)

如果還需要求最終的路徑。那麼在我們的狀態中,還需要記錄,每乙個狀態第一次被訪問到的時候,是由哪乙個 狀態而來的,也就是說把上乙個狀態也記錄下來。這樣最終就可以回溯上去,找到路徑。

如果這個迷宮還有門、鑰匙一類的設定。那麼我們的狀態中,就需要在記當前狀態是不是有鑰匙,是什麼型別的鑰匙等等。

但不論題目怎麼變,廣搜最終的實現是沒有變的。都由佇列來實現。

廣搜很重要的一點就是判重。因為我們只允許乙個狀態走一遍。

拿迷宮問題來舉例。

我們走到了(1,1)這個點以後,可以往(1,2)走,而(1,2)又可以走回到(1,1)。

但此時到達(1,1)一定不是最短距離了。

記住一句話,由廣搜的性質,我們不難看出:第一次到達乙個狀態的時候,就是起始狀態到這個狀態的最短路程了,以後到達這個狀態,不會比這次更短。

因此需要判重。對於乙個已經走過的狀態,我們就沒有必要繼續走了,因為已經有更優的解。

這樣保證了每個狀態只走一遍,也保證了演算法的複雜度足夠優秀。

給定乙個n*m的迷宮。迷宮由通道和牆壁組成,可以向上下左右四個方向移動。求起點到終點所需要的最小步數。

input:

n=10,m=10 (」s「為起點,」g「為終點,」#「為牆壁,「.」為通道)

這個問題中,狀態僅僅是目前所在的位置以及當前所走的步數。所以我們可以使用乙個struct把狀態封裝起來。

struct node

;//node是節點的意思

//在這裡用乙個結構體把狀態封裝起來了

//乙個狀態有三個量 分別是橫座標x 縱座標y 當前步數step

關於結構體(有同學反映你們沒有學過)

簡單介紹一下結構體:

//	簡單介紹一下結構體:

// 結構體封裝成的資料型別和我們平時見到的資料型別宣告的方法一致

node cur;

//意思是宣告了乙個變數cur為node型別

//當然型別的名字和裡面包含的變數可以隨便你們自己取

struct aaa

; aaa num;

//意思是宣告了乙個變數num為aaa型別

//而我們通常使用"."來訪問型別中的量,比如:cur.x

//或者num.c

//(cur.x是int型別,num.c是char型別)

回到題目本身來:

那麼我們可以用下面的**來實現:

#include

const

int n=

105;

char map[n]

[n];

//存所給的地圖

int n,m;

struct nodeque[n*n]

;//用來充當佇列的陣列

bool mark[n]

[n];

//用來判重的陣列,如果(x,y)到達過,那麼mark[x][y]=true

int dx=

;int dy=

;//分別朝著四個方向走

bool

judge

(int x,

int y)

void

bfs(

int sx,

int sy)

;//定義乙個cur cur.x=sx cur.y=sy cur.step=0 (也就是初始狀態)

que[

++r]

=cur;

//把初始狀態放入佇列中

mark[0]

[0]=

true

;//標記這個狀態已經走過了

while

(l<=r)

//如果下乙個狀態可以轉移

//也就是說 下乙個狀態 在地圖中 並且不是牆壁 並且以前沒有走過if(

judge

(nxt.x,nxt.y)

&&!mark[nxt.x]

[nxt.y]

&&map[nxt.x]

[nxt.y]

!='#')}

}}intmain()

}return0;

}

廣搜和深搜一樣都可以遍歷所有狀態,因此需要對所有狀態進行處理時,用兩者都可以。但是遞迴函式可以很簡短地編寫,而且狀態的管理可以更加簡單,所以大多數時候可以用深搜來寫。反之,在求取最短路的時候,深度優先搜尋需要反覆經過同樣的狀態,深搜的效率就會很低,地圖大的話根本跑不出來,所以還是使用廣搜比較好。

從空間角度上來說,廣度優先搜尋要把所有狀態都加到佇列,所以通常需要與狀態數成正比地記憶體空間。反之,深度優先搜尋空間與最大遞迴深度成正比。一般狀態數相比,遞迴深度不會太大,所以可以認為深度優先搜尋更加節省記憶體。

BFS廣度優先搜尋

廣度優先搜尋,利用佇列實現,結束標誌是隊列為空的時候 承接dfs的演算法實現的講例,對於迷宮問題我們也可以採取廣度優先搜尋實現 include iostream include cstdio include cstdlib using namespace std int map 55 55 int ...

bfs廣度優先搜尋

這一課我們來學習圖的另一種遍歷方法 廣度優先搜尋 breadth first search,簡稱 bfs 這是一種連通圖的常用遍歷策略,通常用於求起點到各點的最短路徑,以及求兩點之間的最優路徑等問題。首先我們先來看看廣度優先搜尋的具體方法吧 對於乙個連通圖,我們假設一開始所有頂點均未被訪問,廣度優先...

廣度優先搜尋bfs

bfs即廣度優先搜尋演算法,其是搜尋演算法中的一種。1.dfs常用於尋找是否存在解 其從a節點出發,選取乙個臨近點b,然後不斷深入,在搜尋完b的下屬節點 ehif 後,回到a再搜尋臨近a的c節點,以此類推。2.bfs則用於在最短的時間或最少的移動距離內找到解 其往往從a節點出發,搜尋周圍所有的圍繞節...