1. 程式人生 > >寬度優先搜尋BFS 和深度優先搜尋DFS的比較。。

寬度優先搜尋BFS 和深度優先搜尋DFS的比較。。

首先簡單介紹下題目。就是有9個掛鐘,時間只存在3,6,9,12 這4種狀態對應的 狀態編號是 1,2,3,0,然後給你9種操作時鐘的方式,每次可以事操作的時鐘狀態編號+1,如果編號到了4,就是表示為0。目標就是把9個鬧鐘全部變為0狀態。題目大致是這個意思,不懂的可以看看上面的題目連結。

首先我從我比較熟悉的DFS講起,這是深度優先搜尋,它的優點就是如果目標答案需要很多步驟完成,這樣有機率一次就完成了,不過缺點在於需要每次都是把所有運算元都列舉一遍(0~4)之間,0表示不使用當前操作,但是你還是得在程式碼裡面體現。

 DFS好理解,但是所謂的不必要的堆疊開銷,嚴重影響計算機效能,可能你對於簡單的操作步驟,和複雜的操作步驟,搜尋時間可能相差不大。

而BFS相當於,如果步驟簡單可以很快得出結果,步驟複雜就是很慢。這樣就對應了需要計算的規模,但是程式設計複雜度還是稍高一些,也可能不太理解。

下面我寫兩個demo

DFS 這邊可以不儲存每次的狀態資訊。也就是狀態資訊可以不用陣列儲存起來。有兩種方法處理,不同的狀態1.你可以用公用的全域性變數儲存,但是需要回溯,這個優先,不會錯。2,你可以使用boolean dfs() 用return的方法返回當前結果的值,這個不需要回溯,但是使用boolean 來判斷搜尋是否正確。也可以使用返回值,這個也可以但是個人感覺不好把握。回頭研究下有返回值的DFS

public void dfs(int d){

int (d==k)//如果搜尋深度到了指定的k、直接優先返回,我覺得這樣寫最標準

{

if(sum == target){ //這裡需要判斷是否搜尋到了結果 如果搜尋到了 加一個全域性flag標誌

flag = true;

return ;

}

return;//不管搜尋到 都是返回

}

for(int i =0;i<=3;i++)//注意這邊需要帶0 表示當前節點不使用。

{

sum+ = i*mm[d];//mm[d]表示當前深度的搜尋節點值,可以搜0到3次,0次代表不搜尋,結果放sum裡面

save[d] = i;//這裡用全域性save 儲存當前節點值的次數。儲存結果而已

dfs(d+1);//搜尋下一層

sum-= i*mm[d];//這邊注意回溯下

}

if(flag)//這裡一次搜尋結束判斷是否 全域性flag為true

{

return ; //搜尋到結果可以直接ruturn 或者你可以等全部搜尋完成,記錄下最小值。這邊可以自己發揮。每次搜尋到結果 都和min比較下,如果小就留下,並且更新min的值 和全域性儲存結果的陣列

}

}

//基本上我認為的DFS寫完了,可能有不對的地方。。。

bfs就是利用佇列形式實現,相當於收工模擬,他就是需要每次都要儲存狀態資訊,因為兩個搜尋之間事沒有聯絡的。只有搜尋到了最後結果,才會把當前記錄的拿出來。而且不依賴全域性變數儲存,只需要從自己的變數裡面取就好了。所以對於BFS我們在c語言中需要定義結構體,而不是全域性邊量。但是java中沒有結構體,我們可以利用map存一下吧。因為佇列裡的搜尋每一個都是獨立的個體,所以不能使用全域性變數。我們可以用Hashmap裡面儲存一下幾個值{當前狀態資訊 value可能是陣列list  也可以使用二進位制表示狀態,搜尋了幾步 這個可以用int,每一步搜尋的具體形式(相當於每一步都搜尋了哪些值)這個用陣列} ,每次我們都拿當前狀態資訊比較目標狀態,如果匹配,就可以輸出答案,並且結束迴圈。當然你也可以先結束迴圈,比如先拿陣列第一個元素,比較是否是目標資訊,不是就出佇列。如果是,就return; 然後輸出陣列第一個元素的相關資訊。

優點就是根據資料規模來執行時間,但是儲存的東西太多了。。。。可能會出錯。而且記憶體也佔用太大。 當然肯定有優化,也可以使用全域性來優化,map裡面可以只儲存 步驟和 每一步怎麼操作的。然後根據這些資訊算出狀態。。還不如每次儲存呢。。。。。。

下面給出demo

public void BFS(){

ArrayList my = Arrays.copy(m);//這邊m表示要操作的初始狀態陣列,首先複製一下,不然原始資料沒了

Map first = new HashMap(4);//這邊需要先第一個節點入佇列 這個節點儲存初始資訊

first.add("now_status",my);//這邊是儲存初始狀態

first.add("pos",0);//這邊儲存多少搜尋多少步驟,如果不需要輸出可以不要

first.add("operate",new ArrayList(pos));//這個陣列是運算元組,代表每步搜尋了啥,初始肯定是空

queue.push(first);//這邊自己定義個佇列,然後入佇列

visit[my] = true;

pos=0;//全域性操作步驟

while(!queue.isEmpty()){//佇列不為空,如果為空表示搜尋完了還沒結果

Map a = queue.pop();//取第一個

if(a.get("now_status") == target_status) 

{

  print;//這邊輸出結果 通過 a.get("operate").get(pos);輸出拉 ,步驟肯定是最小的

   return ;

}

pos++;

for(int i =1; i<=9; i++)

{

status = a.get("now_status")+= mm[i];//mm[i]表示每個操作 

if(!visit[status])//新的狀態加入佇列 老狀態不加

{

//構造map

map.put("pos",pos); 

//儲存操作的mm[i]

map.put("operate","")

//存入新的status

map.put("now_status",status );

queue.push(map);

}

}

//結束

}

}

差不多BFS沒了 下面給出我認為比較好的BFS和DFS演算法 複製的哦

DFS

#include <iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<queue> using namespace std;     bool flag[4][4][4][4][4][4][4][4][4]; int op[10][10] = {{0},{1,2,4,5},{1,2,3},{2,3,5,6},{1,4,7},{2,4,5,6,8},                   {3,6,9},{4,5,7,8},{7,8,9},{5,6,8,9}};   int st[10]; struct node {     int state[10];     int pos;//從起始狀態到當前狀態經過的運算元     int ans[200];//從起始狀態到當前狀態所有的操作     friend bool operator < (struct node a,struct node b)     {         return a.pos > b.pos;     } }s,now;   void Bfs() {     priority_queue<node> lcm;     int i,j;     memset(flag,0,sizeof(flag));     for(i = 1;i <= 9;i ++)         s.state[i] = st[i];     s.pos = 0;     flag[st[1]][st[2]][st[3]][st[4]][st[5]][st[6]][st[7]][st[8]][st[9]] = 1;     lcm.push(s);     while(!lcm.empty())     {         now = lcm.top();         lcm.pop();         for(i = 1;i <= 9;i ++)         {             s = now;             for(j = 0;op[i][j];j ++)             {                 s.state[op[i][j]] ++;                 if(s.state[op[i][j]] >= 4)                     s.state[op[i][j]] -= 4; //                s.state[op[i][j]] %= 4;             }                 if(!flag[s.state[1]][s.state[2]][s.state[3]][s.state[4]][s.state[5]][s.state[6]][s.state[7]][s.state[8]][s.state[9]])             {                 s.ans[s.pos ++] = i;                 flag[s.state[1]][s.state[2]][s.state[3]][s.state[4]][s.state[5]][s.state[6]][s.state[7]][s.state[8]][s.state[9]] = 1;                 lcm.push(s);             }             if(!s.state[1] && !s.state[2] && !s.state[3] && !s.state[4] && !s.state[5]                && !s.state[6] && !s.state[7] && !s.state[8] && !s.state[9])             {              //   printf("%d",s.ans[0]);                 sort(s.ans,s.ans + s.pos);//所有的操作互不干擾,無先後之分的                 for(j = 0;j < s.pos;j ++)                     printf("%d ",s.ans[j]);                 printf("\n");                 return;             }             }     } }   int main() {     int i,j;     while(scanf("%d",&j) != EOF)     {         st[1] = j;         for(i = 2;i <= 9;i ++)         {             scanf("%d",&st[i]);         }         Bfs();     }     return 0; }  

BFS

#include<bits/stdc++.h>
using namespace std ;
//Vijos P1016

const int way[9][9] = {{1 , 1 , 0 , 1 , 1 , 0 , 0 , 0 , 0} , {1 , 1 , 1 , 0 , 0 , 0 , 0 , 0 , 0} , {0 , 1 , 1 , 0 , 1 , 1 , 0 , 0 , 0} , {1 , 0 , 0 , 1 , 0 , 0 , 1 , 0 , 0} , {0 , 1 , 0 , 1 , 1 , 1 , 0 , 1 , 0} , {0 , 0 , 1 , 0 , 0 , 1 , 0 , 0 , 1} , {0 , 0 , 0 , 1 , 1 , 0 , 1 , 1 , 0} , {0 , 0 , 0 , 0 , 0 , 0 , 1 , 1 , 1} , {0 , 0 , 0 , 0 , 1 ,1 , 0 , 1 , 1}} ;//9種方式改變不同的鐘,1代表加3點,0代表不變
bool judge ;//判斷是否調整完成
int x[9] , ans[9] ;
void dfs(int num)
{
    if (num == 9)//9種方式全部搜完
    {
        for (int i = 0 ; i < 9 ; i ++)
        {
            if (x[i] % 4 != 0)//有不是12點的鐘
                return ;
        }
        judge = true ;//全部調整完成
        return ;
    }
    for (int i = 0 ; i <= 3 ; i ++)
    {
        ans[num] = i ;//第(i+1)鍾方式的數量
        for (int j = 0 ; j < 9 ; j ++)
        {
            x[j] += way[num][j] * i ;//改變
        }
        dfs(num + 1) ;//深搜
        if (judge)//此時已經調整完成,而調整方式有且只有一種,因此可以輸出答案
            return ;
        for (int j = 0 ; j < 9 ; j ++)
        {
            x[j] -= way[num][j] * i ;//回溯
        }
    }
}
int main()
{
    for (int i = 0 ; i < 9 ; i ++)
    {
        cin >> x[i] ;
    }
    dfs(0) ;
    for (int i = 0 ; i < 9 ; i ++)
    {
        for (int j = 0 ; j < ans[i] ; j ++)
        {
            cout << i + 1 << ' ' ;
        }
    }
    cout << endl ;
}