廣度優先搜尋詳解
1. 也稱寬度優先搜尋,顧名思義,就是將一棵樹一層一層往下搜。
演算法首先搜尋和s距離為k的所有頂點,然後再去搜尋和s距離為k+l的其他頂點。bfs是一種完備策略,即只要問題有解,它就一定可以找到解。並且,廣度優先搜尋找到的解,還一定是路徑最短的解。但是它盲目性較大,尤其是當目標節點距初始節點較遠時,將產生許多無用的節點,因此其搜尋效率較低。需要儲存所有擴充套件出的狀態,占用的空間大。
一般需求最優解的時候用廣搜。
搜尋過程是:從初始節點s0開始逐層向下擴充套件,在第n層節點還沒有全部搜尋完之前,不進入第n+1層節點的搜尋。
假設有兩個表:open表存放待處理節點,closed表存放處理完節點
open表中的節點總是按進入的先後排序,先進入open表的節點排在前面,後進入open表的節點排在後面。
廣度優先搜尋演算法如下:(用 queue)
(1) 把初始節點s0放入open表中;
(2) 如果open表為空,則問題無解,失敗退出;
(3) 把open表的第乙個節點取出放入closed表,並記該節點為n;
(4) 考察節點n是否為目標節點。若是,則得到問題的解,成功退出;
(5) 若節點n不可擴充套件,則轉第(2)步;
(6) 擴充套件節點n,將其子節點放入open表的尾部,並為每乙個子節點設定指向父節點的指標,然後轉第(2)步。
**框架:
bfs()
} dijkstra
單源最短路徑
演算法和prim
最小生成樹
演算法都採用了和寬度優先搜尋類似的思想。其別名又叫bfs,屬於一種盲目搜尋法。
2. 判重
新擴充套件出的節點如果和以前擴充套件出的節點相同,則則個新節點就不必再考慮。設定判重的方法的時候要注意時間和空間的代價。
方案一:每個節點對應於乙個九進製數,則4個位元組就能表示乙個節點。 ( 228< 99=387,420,489 <229)
判重需要乙個標誌位序列,每個狀態對應於標誌位序列中的1位,標誌位為0表示該狀態尚未擴充套件,為1則說明已經擴充套件過了
標誌位序列可以用字元陣列存放。陣列的每個元素存放8個狀態的標誌位。位序列最多需要99位,因此存放位序列的陣列需要99/8 + 1個位元組 48,427,562位元組
如果某個狀態對應於乙個9進製數a,則其標誌位就是標誌位序列中的第a位(其所屬的陣列元素下標是a/8)
方案二:為節點編號
把每個節點都看乙個排列,以此排列在全部排列中的位置作為其編號
排列總數:9!=362880
只需要乙個整數(4位元組)即可存下乙個節點
判重用的標誌陣列只需要362,880位元組即可。
此方案比方案1省空間
此方案需要編寫給定排列求序號和給定序號求排列的函式,這些函式的執行速度慢於字串形式的9進製數到其整型值的互相轉換函式。
時間與空間的權衡
對於狀態數較小的問題,可以用最直接的方式編碼以空間換時間,
對於狀態數太大的問題,需要利用好的編碼方法以時間換空間。
3. 改進
雙向廣度優先搜尋:從兩個方向以廣度優先的順序同時擴充套件。
dbfs演算法從兩個方向以廣度優先的順序同時擴充套件,乙個是從起始節點開始擴充套件,另乙個是從目的節點擴充套件,直到乙個擴充套件佇列中出現另外乙個佇列中已經擴充套件的節點,也就相當於兩個擴充套件方向出現了交點,那麼可以認為我們找到了一條路徑。dbfs演算法相對於bfs演算法來說,由於採用了從兩個跟開始擴充套件的方式,搜尋樹的深度得到了明顯的減少,所以在演算法的時間複雜度和空間複雜度上都有較大的優勢!
dbfs的框架
一、主控函式:
void solve()
二、擴充套件函式
int expand(i) //其中i為佇列的編號(表示q0或者q1)
三、判斷當前擴充套件出的節點是否在另外乙個佇列出現,也就是判斷相交的函式:
int isintersect(i,j) //i為佇列編號,j為當前節點在佇列中的指標
//【線性遍歷的時間複雜度為o(n),如果用hashtable優化,時間複雜度可以降到o(1)】
a*:啟發式搜尋搜尋
a*演算法基本上與bfs演算法相同,但是在擴充套件出一結點後,要根據估價函式對待擴充套件的結點排序,從而保證每次擴充套件的結點都是估價函式最小的結點。
估價函式: f'(n) = g'(n) + h'(n) 這裡f'(n)是估價函式,g'(n)是起點到終點的最短路徑值(也稱為最小耗費或最小代價),h'(n)是n到目標的最短路經的啟發值。
由於這個f『(n)其實是無法預先知道的,所以實際上使用「不在位」數和當前層數之和。
a*演算法的基本步驟
建立乙個佇列,計算初始結點的估價函式f,並將初始結點入隊,設定佇列頭和尾指標。
取出佇列頭(佇列頭指標所指)的結點,如果該結點是目標結點,則輸出路徑,程式結束。否則對結點進行擴充套件。
檢查擴充套件的新結點是否與佇列中的結點重複
若與不能再擴充套件的結點重複(位於佇列頭指標之前),則將它拋棄;
若新結點與待擴充套件的結點重複(位於佇列頭指標之後),則比較兩個結點的估價函式中g的大小,保留較小g值的結點。跳至第五步。
如果擴充套件出的新結點與佇列中的結點不重複,則按照它的估價函式f大小將它插入佇列中的頭結點後待擴充套件結點的適當位置,使它們按從小到大的順序排列,最後更新佇列尾指標
如果佇列頭的結點還可以擴充套件,直接返回第二步。否則將佇列頭指標指向下一結點,再返回第二步。
例子解析:
sicily1050
要求: 給出5個數和乙個目標數,求對5個數用加減乘除四種方法任意計算,能得到的最接近目標數的數。
方法:使用廣度搜尋,在廣搜開始前,先比較當前這5個數中離目標數最接近的數,是否為目前得到的最近距離,是就將最近距離更新,並記錄當前這個得到最近距離的數。然後開始廣搜:從第乙個數開始,計算第乙個數和之後每乙個數進行加減乘除後得到的值替換為第乙個數,然後將第二個數標為已訪問,再重複這個廣搜過程。
最後得到的那個得到最近距離的數,就是要求的答案。
廣搜**:
這裡的判重使用的是乙個陣列,來標記對應的5個數是否已經被訪問。當回退時,記得將標記還原為未訪問。
sicily 1444
每次改變乙個數字,且每次改變後的數依然是素數,找出乙個四位素數最少經過幾次變換可以得到另乙個四位素數。
1. 建素數表
isprime[0] = isprime[1] = false;
for(i = 2; i <= 100; i++)
for(j = i; i*j <= 10000; j++)
if(isprime[i])
isprime[i*j] = false;
2.
bfs過程
搜尋 廣度優先搜尋
廣度優先搜尋一層一層地進行遍歷,每層遍歷都是以上一層遍歷的結果作為起點,遍歷乙個距離能訪問到的所有節點。需要注意的是,遍歷過的節點不能再次被遍歷。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 遍歷標...
廣度優先搜尋
隊 列 1 定義 佇列也是一種運算受限的線性表。在這種線性表上,插入限定在表的某一端進行,刪除限定在表的另一端進行。允許插入的一端稱為隊尾,允許刪除的一端稱為隊頭。特點 佇列中資料元素的入隊和出隊過程是按照 先進先出 的原則進行的。因此,佇列又稱為 先進先出 的線性表,簡稱fifo表。2 實現 鏈佇...