淺談DFS與BFS
搜尋(深搜回溯與廣搜)
1.深搜與回溯
深度優先搜尋,簡稱為深搜或 "DFS" (Depth First Search),
是圖運算的一種搜尋方式,簡要來說是對每一個可能的分支路徑深入到不能再深入為止,而且每個節點只能訪問一次.大致的搜尋過程如下
深度優先遍歷圖的方法是,
從圖中某頂點v出發:
(1)訪問頂點v;
(2)依次從v的未被訪問的鄰接點出發,對圖進行深度優先遍歷;直至圖中和v有路徑相通的頂點都被訪問;
(3)若此時圖中尚有頂點未被訪問,則從一個未被訪問的頂點出發,重新進行深度優先遍歷,直到圖中所有頂點均被訪問過為止。
當然,當人們剛剛掌握深度優先搜尋的時候常常用它來走迷宮.而在走迷宮時,有是我們會走到一條死路中,所以我們不得不退回到上一步,對下一個進行搜尋
void DFS(int 當前狀態) { if(當前狀態為邊界狀態) { 記錄或輸出 return; } for(i=0;i<n;i++) { //擴展出一個子狀態。 修改了全域性變數 if(子狀態滿足約束條件) { dfs(子狀態) } 恢復全域性變數//回溯部分 } }
這個程式碼大體上就是DFS的全過程了,
“一直往下走,走不通了就掉頭,換一條路再往下走”便是深搜的精髓.
深搜的特點:
(1)深度優先搜尋法有遞迴以及非遞迴兩種設計方法。一般的,當搜尋深度較小、問題遞迴方式比較明顯時,用遞迴方法設計好,它可以使得程式結構更簡捷易懂。當資料量較大時,由於系統堆疊容量的限制,遞迴容易產生溢位,用非遞迴方法設計比較好。
(2)深度優先搜尋方法有廣義和狹義兩種理解。廣義的理解是,只要最新產生的結點(即深度最大的結點)先進行擴充套件的方法,就稱為深度優先搜尋方法。在這種理解情況下,深度優先搜尋演算法有全部保留和不全部保留產生的結點的兩種情況。而狹義的理解是,僅僅只保留全部產生結點的演算法。本書取前一種廣義的理解。不保留全部結點的演算法屬於一般的回溯演算法範疇。保留全部結點的演算法,實際上是在資料庫中產生一個結點之間的搜尋樹,因此也屬於圖搜尋演算法的範疇。
(3)不保留全部結點的深度優先搜尋法,由於把擴充套件望的結點從資料庫中彈出刪除,這樣,一般在資料庫中儲存的結點數就是深度值,因此它佔用的空間較少,所以,當搜尋樹的結點較多,用其他方法易產生記憶體溢位時,深度優先搜尋不失為一種有效的演算法。
(4)不一定會得到最優解,這個時候需要修改原演算法:把原輸出過程的地方改為記錄過程,即記錄達到當前目標的路徑和相應的路程值,並與前面已記錄的值進行比較,保留其中最優的,等全部搜尋完成後,才把保留的最優解輸出。
廣搜(BFS)
廣度優先搜尋:寬度優先搜尋演算法(又稱廣度優先搜尋)是最簡便的圖的搜尋演算法之一,這一演算法也是很多重要的圖的演算法的原型。Dijkstra單源最短路徑演算法和Prim最小生成樹演算法都採用了和寬度優先搜尋類似的思想。其別名又叫 BFS ,屬於一種盲目搜尋法,目的是系統地展開並檢查圖中的所有節點,以找尋結果。換句話說,它並不考慮結果的可能位置,徹底地搜尋整張圖,直到找到結果為止。
搜尋的大致過程如這樣:
對每一個子節點的可能節點進行搜尋,
已知圖G=(V,E)和一個源頂點s,寬度優先搜尋以一種系統的方式探尋G的邊,從而“發現”s所能到達的所有頂點,並計算s到所有這些頂點的距離(最少邊數),該演算法同時能生成一棵根為s且包括所有可達頂點的寬度優先樹。對從s可達的任意頂點v,寬度優先樹中從s到v的路徑對應於圖G中從s到v的最短路徑,即包含最小邊數的路徑。該演算法對有向圖和無向圖同樣適用。----百度百科
利用佇列先進先出的特性,把每個點的後繼存入佇列,不斷地彈出,進行判斷.
void BFS()
{ … …//初始化起點入隊
while(!q.empty()) //判斷隊是否為空
{ … …//獲取隊首元素
if(... …)
{… …}//判斷是否是終點
for(int i=0;i<4;i++)//四個方向
{ k.x=p.x+dir[i][0];
k.y=p.y+dir[i][1];
//向各個方向走一步
if(judge())//判斷能不能走
{ … …//各種處理
vis[k.x][k.y]=1; //標記
q.push(k); //入隊
}
}
}
}
一個比較經典的搜尋題目,DFS和BFS都可以做
例題