前段時間遇到了一些與bfs有關的有趣的問題,在一些朋友或者資料的幫助下有所思考,發現這個簡單的演算法如果能應用自如,的確能發揮強大的功效,於是乎寫篇部落格記錄一下。
bfs概念很簡單,此處有介紹;bfs實現也很簡單,用乙個queue就可以了;而它確實也是圖中乙個非常重要的演算法,而它確實也可以用來解決一些看似與圖沒啥明顯關係的問題。 理解廣度優先搜尋,關鍵在於理解其應用。
1. 基本應用
廣度優先搜尋演算法是基於圖定義的,所以最直觀的應用自然就在圖中:有明確的vertex和edge。
比如地圖,某個地點就是vertex,而連線兩個地點的路徑,就是edge,假設edge長度都一樣(當然,事實上這是不可能的),我們就能用廣度優先搜尋求得兩點間的最短距離;
比如人際關係圖,乙個人就是vertex,而兩人是否認識(connected) 就是edge,我們熟知的開心網,linkedin就是這種情況,此時我們應該可以用廣度優先搜尋得到兩個人之間最少可以通過幾個朋友取得聯絡。 據說我們可以通過6個人介紹從而認識**** - 不算太壞。
然而,有很多問題並不是這麼直觀的,你可能看不到乙個圖,看不到bfs的適用性。此時,我們需要足夠的洞察力,將問題抽象到乙個圖上,並選擇正確的目標函式來應用bfs。下面幾個例子分別予以說明。
2. 迷宮問題
迷宮是由乙個乙個的方格仔組成,有些格仔之間是相通的,而有些不是,把乙隻小白鼠放入這樣乙個迷宮,問小白鼠怎麼樣才能最快的走出迷宮vertex:所有值為1的格仔。 (值為0的格仔因為不連通,從邏輯上來講並不屬於這個圖,但我們依賴與它來判斷兩點間是否有edge存在)
edge: 任意兩個相鄰的,且值都為1的vertex之間的聯絡
可以發現,迷宮的這種對於圖的表達方式,與傳統的鄰接表,鄰接矩陣表達方式很不相同 (更加緊湊),但是卻非常適合來表示迷宮這樣的結構。這也是乙個圖,只不過有其特定的訪問方式而已。 指定起點,與終點(任何邊界vertex),
在這個陣列上應用bfs,就能找出走出迷宮的最短路徑,當然,需要注意的是不能重複訪問已經訪問過的結點,我們可以直接修改表示迷宮的陣列,如訪問過了就設為0,也可以提供乙個輔助陣列來標記是否訪問過。
這裡有篇文章詳細的介紹了迷宮問題的解法。
3. 倒酒問題
乙個8l杯子裝滿了酒,有乙個乙個3l空杯子和乙個5l空杯子,問怎樣才能用最少的次數倒出4l的酒。(不能倒掉)這是乙個面試中常見的邏輯題,做到這樣的題,如果不了解其背後的套路,只是像個沒頭蒼蠅一樣不斷的去嘗試,倒來倒去可能會花很多時間,而且不一定是最少的,甚至,還有可能把自己繞暈。
乙個解法是把每次倒完後3個杯子中酒的數量視為乙個狀態,而」倒「的過程則是乙個狀態遷移的過程,初始狀態為800, 狀態遷移則是把杯子a的酒倒入杯子b,結果要求要麼a為空,要麼b為滿。 我們可以有如下推理過程:
對於這個推理過程,要注意兩點:
這裡,800-305-332-602-620-125-134這條路線以一步的優勢勝出!
可以看出,這裡其實就是應用了廣度優先搜尋演算法,對於更加複雜的問題(如5個杯子啥的),完成可以程式設計予以解決。 那麼圖在**?
vertex:某個時刻三個杯子中酒的狀態
edge:可以通過一次倒酒實現的從乙個狀態到另外乙個狀態的」遷移「。
為了標誌某個狀態是否訪問過,我們可以用乙個大小為1000的陣列來表示,預設值為0,某時刻的狀態數值為陣列下標,也即100*a + 10*b + c。(思考,如果有杯子容量大於10呢?)
(感謝atyuwen同學給出了這個思路)
有了這樣的思路,對於解決下面這個過橋問題應該比較容易了:
四個女人過橋,夜間有一火把,每次最多過兩個,必需帶火把,過橋速度不一樣 1min, 2min, 5min, 10min; 兩個人過用最慢乙個的速度,火把不能扔,如何在最快的時間內讓四個女人都過橋?這個問題可以通過思考來解決,原則就是盡量讓快的人在一起,慢的人在一起,避免快的被慢的人拖累(這和多執行緒的負載平衡類似),這樣解法就是:
17分鐘,但是如果人比較多的話,你可能也無法確定是不是最快了。 這個問題其實也可以用圖的bfs演算法解決:
vertex: 在對面的人的狀態,可以用乙個4位的二進位制數表示,沒一為分別對應乙個人的狀態,0000表示乙個都沒過去,1111表示都過去了
edge:兩個人走過去,如果還沒全過去的話,再乙個人回來,從而發生的狀態「遷移」
這樣,演算法應該不難出來了。
4. 連連看
這其實是《程式設計之美》第一章」1.14 連連看遊戲設計「中介紹的乙個演算法,連連看可以由乙個二維陣列表示,陣列元素的值表示不同的圖形,我們可以用0表示該位置沒有圖形。 連連看中兩個結點能夠相連從而消去的標準是:相連不超過兩個彎的相同圖形:
此處, vertex是所有沒有圖形的結點,而edge則是任意兩個在同一直線上的,未被有圖形的結點截斷的結點之間的聯絡。bfs的應用在於結點距離不超過2次,此處距離是指轉彎次數。
通過上訴例子,bfs之強大與有趣可見一斑!
簡單地理解廣度優先搜尋
還是小明找出口的問題,最開始小明在入口 1,1 處,一步可以到達的點有 1,2 和 2,1 如下 但是出口並不在這兩個點上,那麼小明只能通過 1,2 和 2,1 這兩個點繼續往下走,比如現在小明走到了 1,2 這個點,之後他又能到達那些點呢?有 2,2 再看看通過 2,1 又可以到達哪些點呢?可以到...
搜尋 廣度優先搜尋
廣度優先搜尋一層一層地進行遍歷,每層遍歷都是以上一層遍歷的結果作為起點,遍歷乙個距離能訪問到的所有節點。需要注意的是,遍歷過的節點不能再次被遍歷。class solution,int shortestpathbinarymatrix vectorint grid length return 1 cl...
廣度優先搜尋
include include include include using namespace std struct node 圖頂點結構定義 typedef struct node graph 圖形的結構新型態 struct node head 9 圖形頂點陣列 int visited 9 遍歷標...