1. 程式人生 > >C.就來談談深搜與廣搜

C.就來談談深搜與廣搜

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程式碼海洋的同胞們有心人終成正果。
                                                                淚目°