寬度優先搜尋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 ;
}