基于栈(Stack)实现深度优先算法走迷宫

1.问题描述与实现思路

给定下面的迷宫,起点(1,1),终点(8,8),找到一个可能的路径,对于每个位置,约定按照右下左上的顺序查找。使用栈,利用深度优先算法即可找到。

具体操作为从(1,1)开始,Push(1,1),在每个位置P,先判断是否是终点,若是,则完成任务,若不是,则依次判断右下左上有无可行的路径。如果一个新的位置Q(紧邻P)是可以到达的,也即没有墙、也没有走过,那么走到Q,也即Push(Q)。如果P的四周都不能走,那么Pop(P),再次根据上面的要求寻找下一个Q。若最终得到了一个空栈,则问题无解!

  1. Initially: (1, 1);

  2. Push (1, 2): (1, 1)→(1, 2);

  3. Push (2, 2): (1, 1)→(1, 2)→(2, 2);

  4. Push (3, 2): (1, 1)→(1, 2)→(2, 2)→(3, 2);

  5. Push (3, 3): (1, 1)→(1, 2)→(2, 2)→(3, 2)→(3, 3);

  6. Push (3, 4): (1, 1)→(1, 2)→(2, 2)→(3, 2)→(3, 3)→(3, 4);

  7. Push (2, 4): (1, 1)→(1, 2)→(2, 2)→(3, 2)→(3, 3)→(3, 4)→(2, 4);

  8. Push (2, 5): (1, 1)→(1, 2)→(2, 2)→(3, 2)→(3, 3)→(3, 4)→(2, 4)→(2, 5)

  9. Push (2, 6): (1, 1)→(1, 2)→(2, 2)→(3, 2)→(3, 3)→(3, 4)→(2, 4)→(2, 5)→(2, 6);

  10. Push (1, 6): (1, 1)→(1, 2)→(2, 2)→(3, 2)→(3, 3)→(3, 4)→(2, 4)→(2, 5)→(2, 6)→(1, 6);

  11. Push (1, 5): (1, 1)→(1, 2)→(2, 2)→(3, 2)→(3, 3)→(3, 4)→(2, 4)→(2, 5)→(2, 6)→(1, 6)→(1, 5);

  12. Push (1, 4): (1, 1)→(1, 2)→(2, 2)→(3, 2)→(3, 3)→(3, 4)→(2, 4)→(2, 5)→(2, 6)→(1, 6)→(1, 5)→(1, 4);

  13. Pop(1, 4): (1, 1)→(1, 2)→(2, 2)→(3, 2)→(3, 3)→(3, 4)→(2, 4)→(2, 5)→(2, 6)→(1, 6)→(1, 5);

  14. Pop(1, 5): (1, 1)→(1, 2)→(2, 2)→(3, 2)→(3, 3)→(3, 4)→(2, 4)→(2, 5)→(2, 6)→(1, 6);

  15. Pop(1, 6): (1, 1)→(1, 2)→(2, 2)→(3, 2)→(3, 3)→(3, 4)→(2, 4)→(2, 5)→(2, 6);

  16. Pop(2, 6): (1, 1)→(1, 2)→(2, 2)→(3, 2)→(3, 3)→(3, 4)→(2, 4)→(2, 5);

  17. Pop(2, 5): (1, 1)→(1, 2)→(2, 2)→(3, 2)→(3, 3)→(3, 4)→(2, 4);

  18. Pop(2, 4): (1, 1)→(1, 2)→(2, 2)→(3, 2)→(3, 3)→(3, 4);

  19. Pop(3, 4): (1, 1)→(1, 2)→(2, 2)→(3, 2)→(3, 3);

  20. Pop(3, 3): (1, 1)→(1, 2)→(2, 2)→(3, 2);

  21. ......

按照上面的步骤不断进行就可以找到一条路径,限于篇幅不再赘述。

可以分析,最坏情况下复杂度O(N*M)。

2.C语言代码实现

2.1 栈的实现

栈的定义如下:

#include<stdio.h>
#include<stdlib.h>
#include<malloc.h>
#define EmptyTOS -1

struct StackRecord
{
    int Capacity;//栈的容量
    int TopOfStack;//栈顶标志
    T* Array;//栈本身,T需要通过typedef为某一个确定的数据类型
};

typedef struct StackRecord* Stack;

/*栈的基本操作*/
int IsEmpty(Stack S);//判空
int IsFull(Stack S);//判满
Stack CreateStack(int MaxElements);//创建堆栈
Stack DisposeStack(Stack S);//销毁堆栈
void MakeEmpty(Stack S);//置空
void Push(T X, Stack S);//入栈
T Top(Stack S);//获取栈顶元素
T Pop(Stack S);//出栈

栈的基本操作实现:

/*栈的操作实现*/
Stack CreateStack(int MaxElements)
{
    Stack S;
    S = (Stack)malloc(sizeof(struct StackRecord));
    if (S == NULL)
    {
        printf("Out of Space!!!\n");
        return NULL;
    }
    S->Array = (T*)malloc(sizeof(T) * MaxElements);
    if (S->Array == NULL)
    {
        printf("Out of Space!!!\n");
        return NULL;
    }
    S->Capacity = MaxElements;//给容量赋值
    MakeEmpty(S);//清空栈
    return S;
}

void MakeEmpty(Stack S)
{
    S->TopOfStack = EmptyTOS;
}

Stack DisposeStack(Stack S)
{
    if (S != NULL)
    {
        free(S->Array);
        free(S);
    }
    S = NULL;
    return S;
}

int IsEmpty(Stack S)
{
    return S->TopOfStack == EmptyTOS;
}

int IsFull(Stack S)
{
    return S->TopOfStack >= (S->Capacity - 1);
}

void Push(T X, Stack S)
{
    if (IsFull(S))
        printf("Full Stack!!!\n");
    else
    {
        S->TopOfStack++;//标记移动
        S->Array[S->TopOfStack] = X;
    }
}

T Top(Stack S)
{
    if (!IsEmpty(S))
        return S->Array[S->TopOfStack];
}

T Pop(Stack S)
{
    if (IsEmpty(S))
        printf("Empty Stack!!!\n");
    else {
        T X = Top(S);
        S->TopOfStack--;
        return X;
    }

}

以上我们便实现了一个基本的栈。

2.2 深度优先算法走迷宫

首先我们需要定义一个Point结构体来储存迷宫坐标:

struct Position {
    int x;
    int y;
};

typedef struct Position Point;
typedef Point T;//此处类似于C++模板编程,增加栈的泛用性

我们把迷宫本体储存在一个二维数组中,以1代表可行的位置,以0代表墙体,下面的函数实现了迷宫的打印,实际上它也是打印一般矩阵的函数,参数不需要矩阵的行数和列数,只需要一个二级指针即可:

void printMatrix(int** matrix) {
    int row = (int)_msize(matrix) / (int)sizeof(int*) - 2;//计算行数
    int col = (int)_msize(*matrix) / (int)sizeof(int) - 2;//计算列数
    for (int i = 0; i < row; i++) {
        for (int j = 0; j < col; j++) {
            printf("%d ", *(*(matrix + i) + j));
        }
        printf("\n");
    }
}

完成了上面的准备工作,下面就是深度优先算法走迷宫的核心代码,它实际上就是把上文中提到的思路进行了具体实现。进入函数,先计算迷宫的行数和列数,然后创建起点位置的Point结构体入栈,接着开始循环,循环退出条件为空栈,每次进入循环,先判断是否达到终点,若是,则直接break,否则就寻找当前位置的右下左上四个方向那个位置还能够进入,将这个位置入栈,如果不存在可以深入的位置,则说明走到死胡同,出栈。直到最终找到终点或者空栈退出循环。如果有解,那么最终栈里面存在的位置就是一条通路,依次出栈就能够得到结果:

void Solution(int** maze, int x_start, int y_start, int x_end, int y_end) {
    int row = (int)_msize(maze) / (int)sizeof(int*) - 1;//计算行数
    int col = (int)_msize(*maze) / (int)sizeof(int) - 1;//计算列数
    int canSolve = 0;//记录是否有解的标记
    Stack S = CreateStack(row * col);
    Point start = { x_start, y_start};//起点,{x坐标,y坐标}
    Push(start, S);
    while (!IsEmpty(S)) {
        Point temp = Top(S);
        if (temp.x == x_end && temp.y == y_end) {
            canSolve = 1;//有解
            break;
        }
        else {
            *(*(maze + temp.y) + temp.x) = 0;//走过的地方就不能在走了,视作墙壁
        }

        if (temp.x + 1 < col && *(*(maze + temp.y) + temp.x + 1) == 1) {
            Point right = { temp.x + 1,temp.y};
            Push(right, S);
        }//当前位置右侧可行,入栈
        else if (temp.y + 1 < row && *(*(maze + temp.y + 1) + temp.x) == 1) {
            Point down = { temp.x,temp.y + 1};
            Push(down, S);
        }//当前位置下侧可行,入栈
        else if (temp.x - 1 >= 0 && *(*(maze + temp.y) + temp.x - 1) == 1) {
            Point left = { temp.x - 1,temp.y};
            Push(left, S);
        }//当前位置左侧可行,入栈
        else if (temp.y - 1 >= 0 && *(*(maze + temp.y - 1) + temp.x) == 1) {
            Point up = { temp.x,temp.y - 1};
            Push(up, S);
        }//当前位置上侧可行,入栈
        else {
            Pop(S);
        }//无处可去,只能出栈
    }
    if (IsEmpty(S)) {
        canSolve = 0;//空栈,无解
    }
    if (!canSolve) {
        printf("There is no solution to this maze!\n");
    }
    else {
        /*有解,开始回溯*/
        printf("The maze can be solved, the shortest path is (row,column):\n");
        while (!(S->TopOfStack == 0)) {
            Point temp = Pop(S);
            printf("(%d,%d)<--", temp.y, temp.x);
        }
        printf("(%d,%d)\n", Top(S).y, Top(S).x);
    }
}

2.3 人机交互部分

上文中只提到了如何去实现栈和走迷宫,下面给出main函数的实现,其中也包含了从键盘或者文本文件读取迷宫的部分。

int main() {

    char source;
    int is_file = 0;//记录输入来源,1-文本文件,0-键盘
    printf("Please select whether you want to input data from the keyboard or read data from a text file?\nK-form the board\tF-from a file\n");
    while (scanf("%c", &source)) {
        char error;
        if (source == 'F' || source == 'f') {
            is_file = 1;
            /*处理潜在的错误输入*/
            while ((error = getchar()) != '\n' && error != EOF);
            break;
        }
        else if (source == 'K' || source == 'k') {
            while ((error = getchar()) != '\n' && error != EOF);
            break;
        }
        printf("The character you entered is not within the required range, please re-enter!\n");
        while ((error = getchar()) != '\n' && error != EOF);
    }
    if (is_file) {
        printf("Please enter the absolute address of the data file to be read (for example, \"C:\\user\\data.txt\"), and press Enter after entering.\n");
        char file_address[100];
        gets(file_address);
        FILE* file = freopen(file_address, "r", stdin);
        if (file == NULL) {
            printf("Error:Failed to open the file. Please restart the application and check the file path and try again.\n");
            exit(-1);
        }
        printf("The file was successfully opened, and the program will read data from the file.\n");
    }
    if(!is_file)
        printf("Please enter the size of the maze (m rows, n columns). Use a space to separate m and n and press ENTER after entering.\n");
    int m, n;
    while (1) {//接收输入,同时进行一定的错误输入处理
        int return_value = scanf("%d %d", &m, &n);
        if (m > 0 && n > 0 && return_value == 2) {
            break;
        }
        printf("Input error, please input again!\n");
        char error;
        while ((error = getchar()) != '\n' && error != EOF);
    }
    int** maze = (int**)malloc(sizeof(int*) * (m + 2));//此处多申请一个内存位置,防止越界
    if (maze != NULL) {
        for (int i = 0; i < n; i++) {
            *(maze + i) = (int*)malloc(sizeof(int) * (n + 2));
        }
    }
    if (!is_file) {
        for (int i = 0; i < m; i++) {
            printf("Please enter the row %d of the maze(1 represents the pathway, 0 represents walls and obstacles).\n", i + 1);
            for (int j = 0; j < n; j++) {
                int temp;
                scanf("%d", &temp);
                if (!(temp == 0 || temp == 1)) {
                    /*处理可能的错误输入*/
                    printf("Input error, please enter 1 or 0!\n");
                    char error;
                    while ((error = getchar()) != '\n' && error != EOF);
                    --j;
                    continue;
                }
                *(*(maze + i) + j) = temp;
            }
        }
    }
    else {
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                int temp;
                scanf("%d", &temp);
                *(*(maze + i) + j) = temp;
            }
        }
    }
    printf("The maze you is:\n");
    printMatrix(maze);//打印迷宫
    if(!is_file)
        printf("Please the starting point (row, column), row between %d to %d, column between %d to %d.\n", 0, m - 1, 0, n - 1);
    int x_start, y_start;
    while (1) {//接收输入,同时进行一定的错误输入处理
        scanf("%d %d", &y_start, &x_start);
        if (x_start >= 0 && y_start >= 0 && x_start <= n - 1 && y_start <= m - 1) {
            break;
        }
        printf("Input error, please input again!\n");
        char error;
        while ((error = getchar()) != '\n' && error != EOF);
    }
    if(!is_file)
        printf("Please the ending point (row, column), row between %d to %d, column between %d to %d.\n", 0, m - 1, 0, n - 1);
    int x_end, y_end;
    while (1) {//接收输入,同时进行一定的错误输入处理
        scanf("%d %d", &y_end, &x_end);
        if (x_end >= 0 && y_end >= 0 && x_end <= n - 1 && y_end <= m - 1) {
            break;
        }
        printf("Input error, please input again!\n");
        char error;
        while ((error = getchar()) != '\n' && error != EOF);
    }
    printf("The starting point is (%d,%d), the ending point is (%d,%d).\n", y_start, x_start, y_end, x_end);
    Solution(maze, x_start, y_start, x_end, y_end);

    system("pause");
    return 0;
}

上面这段代码对于C语言初学者而言还是有一定参考价值的,其中设计了如何重定向输入流,如何处理用户的非法输入,如何创建和使用二维数组等等。下面让我们看看实际效果。

3.实际运行结果

 上图是一个走一般迷宫的实例,可以分析该程序能够实现任务,并且最后给出路径。

当然,深度优先算法实际上是存在不足的,比如上面的这个迷宫,很明显有更快的路径走过去,但是由于我们设计它按照右下左上的顺序去遍历,结果它就走了一条最远的路径,这个问题实际上可以通过采用广度优先算法,或者在走迷宫中称作“洪水算法”去弥补,详情请见后续文章~

 

当然,对于无解的情况也是可以很轻松应对的。

4.代码与测试用例

写在最后,我本人在学习过程中也经常使用CSDN查资料,但是发现很多资源都需要开会员或者付费才能下载,我个人很不喜欢这种方式,当然,我不是不认可为知识付费这一行为,只是更喜欢开源精神,因此,最后也把本文中完整的代码和测试用例附上,希望能够帮到你~

 

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
好的,很高兴回答你的问题。 在C语言中,实现深度优先搜索算法来解决迷宫问题,可以按照以下步骤进行: 1. 定义一个二维数组来表示迷宫,0表示通路,1表示障碍物或墙壁。 2. 定义一个,用来存储走过的路径。 3. 定义一个结构体来表示一个点的坐标。 ```c typedef struct { int x; int y; } Point; ``` 4. 定义一个函数,将起点入,并将其标记为已访问。 ```c void dfs(int maze[][MAX_SIZE], int visit[][MAX_SIZE], Point start, Point end, Stack *stack) { push(stack, start); visit[start.x][start.y] = 1; } ``` 5. 定义一个函数,判断当前点是否为终点,如果是,则输出路径并退出程序。 ```c void dfs(int maze[][MAX_SIZE], int visit[][MAX_SIZE], Point start, Point end, Stack *stack) { ... if (start.x == end.x && start.y == end.y) { printPath(stack); exit(0); } ... } ``` 6. 定义一个函数,遍历当前点的上下左右四个方向,如果该方向为通路且未被访问过,则将该点入,并将其标记为已访问。 ```c void dfs(int maze[][MAX_SIZE], int visit[][MAX_SIZE], Point start, Point end, Stack *stack) { ... // 上 if (start.x > 0 && maze[start.x - 1][start.y] == 0 && visit[start.x - 1][start.y] == 0) { Point next = {start.x - 1, start.y}; push(stack, next); visit[next.x][next.y] = 1; dfs(maze, visit, next, end, stack); pop(stack); } // 下 if (start.x < MAX_SIZE - 1 && maze[start.x + 1][start.y] == 0 && visit[start.x + 1][start.y] == 0) { Point next = {start.x + 1, start.y}; push(stack, next); visit[next.x][next.y] = 1; dfs(maze, visit, next, end, stack); pop(stack); } // 左 if (start.y > 0 && maze[start.x][start.y - 1] == 0 && visit[start.x][start.y - 1] == 0) { Point next = {start.x, start.y - 1}; push(stack, next); visit[next.x][next.y] = 1; dfs(maze, visit, next, end, stack); pop(stack); } // 右 if (start.y < MAX_SIZE - 1 && maze[start.x][start.y + 1] == 0 && visit[start.x][start.y + 1] == 0) { Point next = {start.x, start.y + 1}; push(stack, next); visit[next.x][next.y] = 1; dfs(maze, visit, next, end, stack); pop(stack); } ... } ``` 7. 在主函数中调用dfs函数,并传入起点、终点、迷宫数组和访问数组。 ```c int main() { int maze[MAX_SIZE][MAX_SIZE] = {...}; int visit[MAX_SIZE][MAX_SIZE] = {0}; Point start = {0, 0}; Point end = {MAX_SIZE - 1, MAX_SIZE - 1}; Stack *stack = createStack(); dfs(maze, visit, start, end, stack); return 0; } ``` 以上就是用深度优先搜索算法实现自动走迷宫C语言代码。希望能对你有所帮助。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

mzh1124

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值