C.就來談談深搜與廣搜
阿新 • • 發佈:2018-12-31
C.深搜與廣搜的基礎程式碼實現
下面以一道簡單的搜尋演算法題為例:
在一個,迷宮中尋找出口,並且要求路徑最短。且迷宮中有障礙物不能穿過,並由規定起點出發。輸入包括三部分,第一行:迷宮的尺寸大小。
第二行:定義迷宮。 第三行:起始位置和出口所在。
深搜程式碼實現部分:
#include<stdio.h>
int n,m,p,q,min=9999;
int a[51][51],book[51][51];//a用來儲存迷宮,book用來記錄已走過的路徑。
int sum;//記錄所有出口的方式。
void dfs(int x,int y,int step);//深搜函式。
int main()
{
int i,j,startx,starty;
scanf("%d %d",&n,&m);//輸入迷宮大小。
for(i=1;i<=n;i++)
for(j=1;j<=m;j++)
scanf("%d",&a[i][j]);//編輯迷宮。
scanf("%d %d %d %d",&startx,&starty,&p,&q);//初始座標和出口賦值。
book[startx][starty]=1;//初始座標已走過避免再次搜尋。
dfs(startx,starty,0 );//傳遞引數。
printf("%d\n",min);
printf("%d\n",sum);
system("pause");
return 0;
}
void dfs(int x,int y,int step)
{
int next[4][2]={{0,1},{1,0},{0,-1},{-1,0}};//分別定義上下左右四個方向。
int tx,ty,k;//[tx][ty]表示下一步座標,k為了遍歷所有方向
if(x == p && y == q)//判斷是否為出口
{
if(step < min)
{
min = step;//更新最短路徑
}
sum++;//找到出口sum+1
return;
}
for(k=0;k<=3;k++)
{
tx = x + next[k][0];
ty = y + next[k][1];
if(tx < 1 || tx > n || ty < 1 || ty > m)//如果越界不再進行下一步,直接尋找其他未越界的方向
{
continue;
}
if(a[tx][ty] == 0 && book[tx][ty] == 0)//判斷該方向是否可以繼續行走
{
book[tx][ty] = 1;//記錄當前座標已走過
dfs(tx,ty,step+1);//在當前基礎上進行下一步搜尋
book[tx][ty] = 0;//遍歷過該方向便收回以方便其左邊搜尋時可以行走
}
}
return;
}
以上為深搜的實現程式碼,帶有註釋應該是不難理解的。那麼下面就來看看廣搜吧,並從中發現他們的不同所在吧。
廣搜程式碼實現部分:
#include<stdio.h>
struct note//佇列
{
int x;//橫座標
int y;//縱座標
int f;//父節點(上一個結點)
int s;//步數
};
int main()
{
struct note que[2501];//定義佇列
int a[51][51]={0},book[51][51]={0};//定義迷宮陣列,book用於記錄哪些點已遍歷過
int next[4][2]={{0,1},{1,0},{0,-1},{-1,0}};//分別代表向右,想下,向左,向上
int head,tail;//head表示正擴充套件的結點,tail位於隊尾最後一個元素的後面隨時將head擴展出來的結點入隊
int i,j,k,n,m,startx,starty,p,q,tx,ty,flag;
scanf("%d %d",&n,&m);//迷宮規格
for(i = 1;i <= n;i++)
for(j = 1;j <= m;j++)
scanf("%d",&a[i][j]);
scanf("%d %d %d %d",&startx,&starty,&p,&q);//起點,終點
head = 1;
tail = 1;
//將初始入口入隊
que[tail].x = startx;
que[tail].y = starty;
que[tail].f = 0;
que[tail].s = 0;
tail++;//每執行一次入隊就後移一位
book[startx][starty] = 1;//起點已到達過
flag = 0;//如找到終點即flag=1,現在表示未找到終點
while(head < tail)//佇列不為空
{
for(k = 0;k < 4;k++)//列舉4個方向
{
tx = que[head].x + next[k][0];
ty = que[head].y + next[k][1];
if(tx < 1||tx > n||ty < 1||ty > m)//判斷是否越界
continue;
if(a[tx][ty] == 0 && book[tx][ty] == 0)//判斷是否為障礙,是否已經走過
{
book[tx][ty] = 1;//記錄當前節點已走過,廣搜每個節點只搜尋一次,所以之後不需要再次置零
que[tail].x = tx;
que[tail].y = ty;
que[tail].f = head;
que[tail].s = que[head].s + 1;
tail++;//沒入隊一次tail++
}
if(tx==p && ty==q)//是否找到出口
{
flag = 1;
break;
}
}
if(flag == 1)
break;
head++;//當前節點的擴充套件未有找到出口,搜尋下一個節點
}
printf("%d\n",que[tail - 1].s);//因為tail指向隊尾最後一個元素的後一位
system("pause");//程式執行完畢,暫停檢查資料
return 0;
}
以上就是兩種搜尋的基本實現,從中不難看出:
深搜是屬於“不撞南牆不回頭”?這麼說應該對的哈。總之,深搜大抵就是,有一個結點開始,選擇一條分支進行深搜,當遇到下一結點重複上述步驟,知道這一分支被搜尋完畢,然後再從搜尋完的最下面的結點,向上回溯,如果還有其他選擇,在對其他選擇進行搜尋。大體流程就是這樣,從樹的根部搜尋到最末尾的分支,然後,一個節點搜尋完畢後,回溯,再搜尋,知道在回溯到樹的根部,再由樹的根部發出另一分支。搜尋到目標則停止。
廣搜就有別於深搜,廣搜是向外發散式的搜尋。就像Wi-Fi的圖示,大家都是熟悉的吧,由樹的根部也就是Wi-Fi的那個點一層一層的向外發散,搜尋完一層,再進入下一層,直至到達最後一層。相對於深搜來說,他更顯得盲目。但卻並不花費更多時間,相對來說,深搜更加浪費時間。
寫在最後
搜尋題大體都是這樣的模板,如果遇到問題仔細考慮,判斷用什麼搜尋方法。祝願各位奮鬥在C程式碼海洋的同胞們有心人終成正果。
淚目°