如何用棧實現深度優先算法-C語言解決迷宮問題

數據結構一直都是專業課裏面比較難的一門課程,因爲裏面涉及到了很多算法知識。這也給大家造成了一個困擾,是不是智商不行就學不了數據結構?

顯然不是!算法知識確實很難,但是我們在學習的過程中很少會去開發新的算法,基本上都是在別人的成果上加以探索。其實只要是願意花時間,善於歸納總結,我們還是能發現很多算法的規律。看的多了,就很容易在解決實際問題的時候聯想到對應的算法。

直奔主題吧。棧結構我們在之前的課堂上給大家講過,還寫了用棧解決四則運算的代碼,代碼不簡單,我相信也讓不少小夥伴奔潰。我們今天繼續用棧來解決問題,而且解決的是一個非常著名的算法–深度優先算法(Depth First Search),簡稱DFS。

DFS是圖裏面的一種遍歷算法。提到圖,大家又被嚇的一激靈,這可是比樹還要難的模型啊。別緊張,圖確實很難,但是早就有人把它總結出來了,只要套用固定的算法,還是可以解決的。我們還是先看看DFS是怎麼回事吧。

DFS簡要來說是對每一個可能的分支路徑深入到不能再深入爲止,而且每個節點只能訪問一次,等到發現不能再深入了,再回頭換一條路探索。啥玩意?看看下面的例子:

在這裏插入圖片描述對於這種結構,DFS方法首先從根節點1開始,其搜索節點順序是1,2,3,4,5,6,7,8。

步驟如下:
1、訪問第一個節點,並且把第一個節點入棧

在這裏插入圖片描述2、找出與此點鄰接的且尚未遍歷的點,進行標記,然後放入stack中,依次進行;

在這裏插入圖片描述
3、如果此點沒有尚未遍歷的鄰接點,則將此點從stack中彈出,再按照2依次進行。比如第5個節點進棧後,找不到合適的下一個節點,把第5個節點彈出,再去尋找第4個節點的另一條出路。

在這裏插入圖片描述
在這裏插入圖片描述
4、直到遍歷完整個樹,stack裏的元素都將彈出,最後棧爲空,DFS遍歷完成。

在這裏插入圖片描述
在這裏插入圖片描述
我們再來看一個深度優先算法的典型應用:迷宮問題。簡單來看一下問題描述,然後直接上代碼。

問題描述:

以一個m*n的長方陣表示迷宮,0和1分別表示迷宮中的通路和障礙。迷宮問題要求求出從入口(1,1)到出口(m,n)的一條通路,或得出沒有通路的結論。基本要求:首先實現一個以鏈表作存儲結構的棧類型,然後編寫一個求迷宮問題的非遞歸程序,求得的通路,其中:(x,y)指示迷宮中的一個座標,dir表示走到下一座標的方向。左上角(1,1)爲入口,右下角(m,n)爲出口。

#include <stdio.h>

#define SIZE   1024

struct Box 
{
    int x;              //點的橫座標
    int y;              //點的縱座標
    int dir;            //下一個點的方向
};
typedef struct Box Box;

typedef struct
{
    Box data[SIZE];
    int top;
}Stack;

//用二位數組表示迷宮,0表示可以走,1表示不可以走
int map[6][6] = { 
    {0, 0, 0, 1, 0, 1}, 
    {0, 1, 0, 0, 1, 0}, 
    {0, 0, 1, 0, 0, 0}, 
    {0, 1, 0, 1, 1, 0}, 
    {0, 1, 0, 1, 0, 0}, 
    {0, 0, 0, 0, 0, 0}};

//初始化順序棧
int InitStack(Stack *s) 
{
    if (!s)
        return -1; 
    s->top = -1;
    return 0;
}

//檢查該點是否可以走,1表示不能走,超出地圖也不能走
int check(Box b)
{
    if (b.x < 0 || b.x > 5 || b.y < 0 || b.y > 5)   //點不在迷宮內
        return -1;
    if (map[b.x][b.y] != 0)                         //點不能走
        return -1;
    return 1;
}

//進棧操作
int push(Stack *s, Box b)
{
    if (s->top == SIZE - 1 || !s)
        return -1;
    s->top++;
    s->data[s->top] = b;
    return 0;
}

//判斷棧是否爲空
int EmptyStack(Stack *s)
{
    if (!s)
        return -1;
    return (s->top == -1) ? 1 : 0;
}

//獲取棧頂元素
int GetTop(Stack *s, Box *b)
{
    if (!s || s->top == -1)
    return -1;
    *b = s->data[s->top];
    return 0;
}

//顯示一條可以走得通的路徑(路徑保存在棧中,遍歷棧可以得到路徑)
void ShowPath(Stack *s)
{
    int i;
    for (i = 0; i <= s->top; i++)
    {
        printf("(%d %d)", s->data[i].x, s->data[i].y);
        if (i != s->top)
        {
            printf("->");
        }
    }
    printf("\n");
}

//出棧操作
int pop(Stack *s, Box *b)
{
    if (!s || s->top == -1)
        return -1;
    *b = s->data[s->top];
    s->top--;
    return 0;
}

//修改棧頂元素,下一個點的位置
int ChangeDir(Stack *s, int dir)
{
    if (!s || s->top == -1)
    return -1;
    s->data[s->top].dir = dir;
    return 0;
}

//x1 y1表示起點   x2,y2表示終點
int Walk(Stack *s, int x1, int y1, int x2, int y2)
{
    Box now, t;
    int i, j;

    now.x = x1;
    now.y = y1;
    now.dir = -1;

    if (check(now) == 1)
    {
        push(s, now);                  //如果該點可以走,則進棧
        map[now.x][now.y] = -1;        //表示點走過
    }

    while (EmptyStack(s) != 1)
    {
        GetTop(s, &now);               //獲取棧頂的點
        if (now.x == x2 && now.y == y2)    //棧頂元素就是終點
        {
            ShowPath(s);
            map[now.x][now.y] = 0;         //表示點沒走過
            pop(s, &now);
            GetTop(s, &now)}
        else
        {
            int k;
            for (k = now.dir + 1; k < 4; k++)        //判斷每個方向是否可走
            {
                switch(k)
                {
                    case 0:                //向上走
                        i = now.x - 1;
                        j = now.y;
                        break;
                    case 1:
                        i = now.x;
                        j = now.y + 1;
                        break;
                    case 2:               //向下
                        i = now.x + 1;
                        j = now.y;
                        break;
                    case 3:              //向左
                        i = now.x;
                        j = now.y - 1;
                        break;
                }
                t.x = i;
                t.y = j;
                t.dir = -1;
                if (check(t) == 1)
                {
                    ChangeDir(s, k);
                    push(s, t);
                    map[i][j] = -1;
                    break;
                }
            }
            if (k == 4)    //沒有方向可以走
            {
                pop(s, &now);    //無路可走,出棧
                map[now.x][now.y] = 0;
            }
        }
    }
}

int main()
{
    Stack stack;

    InitStack(&stack);

    Walk(&stack, 0, 0, 5, 5);

    return 0;
}

DFS也是比較符合人的正常思維的算法,沿着一條路一直走下去,發現行不通了再回來,尋找下一個出路。比如上面的迷宮問題其實就是這樣解決的。當然如果我們用遞歸算法也能解決,思想都是一樣的。大家再平時的學習過程中也要多去積累一些案例,當你看到一個面試題的時候,如果能夠立馬想到這個問題用DFS來解決,那題目就會變得非常簡單,但是如果你想不到,用自己的辦法解決,那就會麻煩的多。

趕緊把上面的代碼敲一遍吧!

更多精彩視頻、文章、嵌入式學習資料,微信關注公衆號 『學益得智能硬件』

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章