如何用栈实现深度优先算法-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来解决,那题目就会变得非常简单,但是如果你想不到,用自己的办法解决,那就会麻烦的多。

赶紧把上面的代码敲一遍吧!

更多精彩视频、文章、嵌入式学习资料,微信关注公众号 『学益得智能硬件』

在这里插入图片描述

发布了14 篇原创文章 · 获赞 26 · 访问量 2683

猜你喜欢

转载自blog.csdn.net/xiaopengX6/article/details/104599137
今日推荐