1. 程式人生 > >DFS(深度優先搜尋),BFS(廣度優先搜尋)小總結

DFS(深度優先搜尋),BFS(廣度優先搜尋)小總結

這周正在學習DFS和BFS。
體驗中覺得這兩個演算法還是挺好用的,但是遇到某些資料大的情況就很容易超時。(所以到後面還是得學習一下如何優化,或者採用更佳的搜尋方法來解決問題)
然後學習了一段時間,感覺基本上了解了DFS和BFS的基礎實現原理以及應用(不過我認為還是得通過做題來培養自己的感覺,什麼時候該採取DFS,什麼時候該採用BFS),它們兩者間各自的優勢需要通過實際的問題來具體分析,根據它們各自的特點來應用於不同的問題中才能獲得最優的效能。

總的來說,兩者都是搜尋方法 (這不是廢話嘛!) ,但是兩者的搜尋方式卻有極大的區別。
我將DFS比作莽夫,就是一條路莽下去,如果有分支路,就選擇其中一條走下去,直到走到終點或者該點處沒有路可以走了,這時候只能回頭,走之前沒有走過的分支,繼續莽下去。
而BFS更像是水流。我們在最上層倒水下來,水會流向這個節點處的各個分支,逐層地開始搜尋。

由於DFS的特性,我們會採用 遞迴的方式來實現。
具體模板:

void dfs()//引數用來表示狀態,例如臨界條件,達到終點的條件  
{  
    if(到達終點狀態)  
    {  
        ...//根據題意新增  
        return;  
    }  
    if(越界或者是不合法狀態)  
        return;  
    if(特殊狀態)//剪枝(避免搜尋不必要的地方)
        return ;
    for(擴充套件方式)  
    {  
        if(擴充套件方式所達到狀態合法)  
        {  
            修改操作;
//根據題意來新增 tag標記; dfs(); 還原tag標記; // dfs很重要一點就是可能要採用回溯思想,視題目而定 } } }

例題: HDU 1241 油田問題
AC程式碼如下:

#include <iostream>
using namespace std;
const int maxn=1e3;
char a[maxn][maxn]; // 用來儲存油田矩陣
int dir[8][2]={{0,1},{0,-1},{1,0},{-1,0},{1,
1},{-1,1},{1,-1},{-1,-1} }; //八個方向 int m,n; //m是行 n是列 void dfs(int x,int y) { int tx,ty; for(int i=0;i<8;i++) { tx=x+dir[i][0]; //tx,ty分別是 下一個座標代表的x,y; ty=y+dir[i][1]; if( tx>=0 && tx<=m && ty>=0 && ty<=n ) //說明沒有越界那麼就繼續搜下去 { if(a[tx][ty]=='@') //使@變成* { a[tx][ty]='*'; dfs(tx,ty); //繼續dfs深搜 } } } } int main() { while( cin>> m >> n && m && n ) { for(int i=0;i<m;i++) cin>>a[i]; int sum=0; for(int i=0;i<m;i++) for(int j=0;j<n;j++) { if(a[i][j]=='@') //如果遍歷到一個@ 說明找到一個油田塊 { a[i][j]='*'; sum++; dfs(i,j); //消除油田 } } cout<<sum<<endl; } return 0; }

BFS 基本實現就採用 佇列的方式

queue< pos > q;
// pos是一個struct 帶有 x,y座標 (用來表示二維陣列的下標)
q.push(首節點);
while(!q.empty())
{
    pos temp=q.front();
    q.pop();
    isok[temp.x][temp.y]=true; //標記該點已經走過了
    for(int i=0;i<4;i++)
    {
        int tx=temp.x+dir[i][0];
        int ty=temp.y+dir[i][1];
        if( tx < n && tx>=0 && ty >=0 && ty < m && !isok[tx][ty] && s[tx][ty]=='.')
        {
            isok[tx][ty]=!isok[tx][ty]; //標記該點已經走過了
            q.push(pos(temp.x+dir[i][0],temp.y+dir[i][1])); // 將這個節點下的子節點插入至佇列中。
        }
    }