DFS&BFS入門

 以下內容約是入門內容的一半吧。  

簡而言之,bfs和dfs 是對於在樹或者圖之類結構上的數據進行搜索得出想要的答案的搜索方法。不同的是,如果把搜索對象範圍比喻成爲一個多分支的洞穴的話,dfs就是放一隻由程序跟蹤的老鼠進去,走到每個盡頭再返回繼續探索下一個,而bfs就是從洞口向裏面倒由程序跟蹤的水,同步進行到各個分枝洞穴。

1.關於DFS。

其函數常常會是這樣的:

void dfs(int start){

    if(no)

        return;

    Dosomethinginteresting();圖片

    for(i=0;i<n;i++)

        if(ok)

            {

                // visited=1;

                dfs(next);

                // visited=0;

            }

}

其實際執行情況就如同右圖。紅色箭頭就表示dfs程序的執行過程。對於深搜dfs,具體描述應該是:

(1)指定一個當前節點,標記已經訪問。

(2)把當前節點的每個未被訪問的子節點當做當前節點進行遞歸,執行(1)。

(3)不滿足條件時進行回溯。

 

由於深搜在進行過程中都是沿着數據結構的指定方向前進的,所以能夠很清楚的記錄下來走過的路以及走過的時間。但是,在數據量太大的時候,由於用的是遞歸的單線搜索,很容易出現運行時間很長的情況。而對於求最短路徑的問題,dfs需要枚舉所有可能的路徑然後進行比較,過程會比較繁瑣。

 

爲了使得dfs運行時間更快,有一個常用方法那就是剪枝。剪枝在程序裏面的體現往往就是一個if(no) break; 或者if(no) return;剪枝規避了一些不符合要求的深搜路徑使得程序不會去走那些已經知道是錯誤的路。比如有時候題目要求在偶數步數走到一個距離奇數步數的節點,這是不可能的,所以不需要進行任何深搜。

另外,記憶化也是一個有效的改進方法。對於在一個相互連接的圖樣結構中,各個路徑有可能訪問相同的節點,那麼爲了避免走同樣的路,第一次走過這個節點的時候就可以將它的值存在一個數組之中,那麼就可以避免重複遞歸了。程序實現就是定義二維數組a[m][n],初始化全部爲0。當程序走到節點(m,n)時,如果a[m][n]==0,那麼把值存進去,如果不爲零,直接讀取使用即可。

2、關於BFS

其函數大概是這樣的:

void BFS(? asd)

{

    queue<?> a;//? 爲一種數據類型

    a.push(asd);

    while(1)

    {

        ? point = a.front();

        if(a yes )

        {

            DoSomethingExciting();

            break;

        }

        for(i=0;i<n;i++)

        if(yes){

            ? fd = initial();

            a.push(fd);

        }

        if(!a.empty())

            a.pop();

        if(a.empty())

        {

            DosometingBad();

            break;

        }

    }

}

 

上面的僞代碼用的是隊列結構來存儲過程變量。也可以用數組結構來儲存。

如果用數組來存儲的話,這裏就需要用到兩個關鍵變量num 和 low。

low=now=0;

while(1)

{

    int th = low;

    if(yes(asd[th])){

        Dosomethingmagic();

break;

}

    for(i=0;i<n;i++)

        if(yes)

            asd[++now]=deal(asd[th]);

    if(low>now)

        break;

    low++;

}

期中asd[]就是類似於隊列的存在。圖片

上圖就是bfs的執行路徑。

具體來說,BFS執行過程如下:

(1)如果隊列不是空的,那麼彈出一個a記爲當前節點。

(2)將當前節點的所有未被訪問的子節點全部存入隊列中。執行(1)


廣搜最適用的情況就是求最短路徑的題目。由於是各個支線同步進行,它能夠最快的找到目標。但是也是因爲如此,它無差別記錄了所有節點會佔用較大的內存。另外,由於沒有從遵守目標結構的順序,所以在獲取路徑的時候需要額外的操作量。即每一個節點必須要存儲上一個節點的位置。另外在數據量比較大的時候,應該把隊列定義位全局變量,以免棧溢出。

 

BFS一個簡單的優化就是雙向廣搜,在知道目標點和初始點時適用。在一個圖類結構中,現在定義兩個隊列,一個以初始點爲開始點,一個以結束點爲開始點,兩個隊列同時進入循環。只要有一個隊列爲空,那麼就知道無法達到,可以直接跳出循環。然而這裏有一個問題就是,兩個搜索指針是很難走到一起,那麼我們必須知道搜索的節點是否已經被對方搜索過了,如果題目對象僅僅佔用一個節點,那麼標記是很簡單的;如果題目對象佔用多個節點或者其他複雜情況那麼判斷起來會有一些難度。

另外,在廣搜節點的儲存之前加上if判斷語句來剪枝也是和dfs類似的,不再敘述。

 

總結:

bfs佔空間,dfs佔時間,這是個令人頭疼的問題。但是,做搜索類題目還有一個困難就是讀題。如何把題目的研究對象抽象成一段代碼,如何把題目對象的研究範圍抽象成爲一種數據結構,我覺得這是解題的基礎也是關鍵。


一個簡單的實例:
一個m*n的座標圖上
給定開始點@,
#代表不能走的路,只能上下左右移動,問在輸入的圖中最多能走到多少塊磚頭上。

 

DFS版本:

 

#include<iostream>
using namespace std;
char map[21][21]={'\0'};
int wayi[4]={0,-1,0,1};
int wayj[4]={1,0,-1,0};//左上右下
int num=0;int a,b;
int yes=1;
void go(int xi,int xj){
if(yes)
for(int i=0;i<4;i++)
{
if( xi+wayi[i] >= 0 && xi+wayi[i] < b && xj+wayj[i] >= 0 && xj+wayj[i] < a )
if(map[xi+wayi[i]][xj+wayj[i]]!='#') {
if(map[xi+wayi[i]][xj+wayj[i]]=='.')
num++;
map[xi+wayi[i]][xj+wayj[i]]='#';
if(num==a*b)
yes=0;
go(xi+wayi[i],xj+wayj[i]);
}
}}
 
int main(){
int starti,startj;
while(cin>>a>>b&&a!=0&&b!=0)
{
num=0;yes=1;
int i,j;
for(i=0;i<b;i++)
for(j=0;j<a;j++)
{
cin>>map[i][j];
if(map[i][j]=='@')
{
starti=i;
startj=j;
}
}
map[starti][startj]=',';
go(starti,startj);
cout<<num+1<<endl;
}
return 0;
}

#include<iostream>
using namespace std;
char map[21][21]={'\0'};
int wayi[4]={0,-1,0,1};
int wayj[4]={1,0,-1,0};
int num=0;
int a,b;
int zhizhen=0;
int savei[400]={0};
int savej[400]={0};
int main(){
while(cin>>a>>b&&a!=0&&b!=0)
{
num=0;zhizhen=0;
int i,j;
for(i=0;i<b;i++)
for(j=0;j<a;j++) {
cin>>map[i][j];
if(map[i][j]=='@')
{
savei[num]=i;
savej[num]=j;
}
}
map[savei[num]][savej[num]]='#';
while(1)
{
int xi=savei[zhizhen],xj=savej[zhizhen];
for(i=0;i<4;i++)
{
if( xi+wayi[i] >= 0 && xi+wayi[i] < b && xj+wayj[i] >= 0 && xj+wayj[i] < a )
if(map[xi+wayi[i]][xj+wayj[i]]!='#')
{
savei[++num]=xi+wayi[i];
savej[num]=xj+wayj[i];
map[xi+wayi[i]][xj+wayj[i]]='#';
}
}
zhizhen++;
if(zhizhen>num)
break;
}
cout<<num+1<<endl;
}
return 0;
}


 

BFS版本

推薦一個詳細解說的網站http://www.cppblog.com/menjitianya/archive/2015/10/09/211980.html 

發佈了25 篇原創文章 · 獲贊 14 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章