栈应用实例--迷宫问题

调试环境:vs2015+win10

众所周知,栈是一个非常常见且有用的数据结构。
这里讲解一下利用栈来实现迷宫问题。

使用递归实现

假设有一迷宫
《栈应用实例--迷宫问题》
其中:1代表墙;0代表路径
为简化编程,假设左边界0处为迷宫入口,下边界0处为迷宫出口库。

分析问题:

  • 创建一个结构体,表示在迷宫的坐标
  • 从文件中读取迷宫
  • 获取迷宫路径,需要判断是否可走,将走过的地方标记为2

代码实现:

#include<assert.h>
#include<stack>
#include<iomanip>
#include<iostream>
using namespace std;

struct Pos {
    int _row;                     //行
    int _col;                     //列
    Pos& operator+(Pos& pos) {    //重载加法运算
        _row += pos._row;
        _col += pos._col;
        return *this;
    }
};

//获取地图
void GetMaze(int* maze) {//为什么不传二维数组,因为二维数组在传参时需要声明个数,这样传参简单实用
    FILE* fp = fopen("MazeMap1.txt", "r");    //读取地图文件
    assert(fp);                               // 断言
    size_t i = 0;
    char c = 0;
    while ((c = fgetc(fp)) != EOF) {          //将从文件读取的一个字符赋值给c,并判断是否是文件尾
        if (c == '1' || c == '0') {           //c如果是墙或是路
            *(maze + i) = c - '0';            //满足就将该字符存入数组
            ++i;
        }
    }
}
//打印地图
void PrintMaze(int* maze, size_t N) {
    assert(maze);
    for (size_t i = 0; i < N; ++i) {         //利用二维的方式访问maze
        for (size_t j = 0; j < N; ++j) {
            cout << maze[i*N + j] << " ";    //打印该节点的值
        }
        cout << endl;                        //换行
    }
}
//判断是否可走
bool CheckIsAccess(int* maze, size_t N, Pos pos) {
    assert(maze);
    if ((pos._col >= 0) && (pos._col < N) && (pos._row >= 0) && (pos._row < N)) {//判断是否不为边界
        if (maze[pos._row*N + pos._col] != 1) {        //判断是否是墙
            if (maze[pos._row*N + pos._col] == 0) {    //判断是否是路
                return true;                           //满足返回true
            }
        }
    }
    return false;                                      //不满足返回false
}
//获取地图路径
void GetMazePath(int* maze, size_t N, Pos entry) {
    assert(maze);
    Pos test[4] = { { -1,0 },{ 0,1 },{ 1,0 },{ 0,-1 } };//声明一个Pos数组,分别表示向上,右,下,左偏移一个单位
    Pos cur = entry;                                    //复制当前节点为cur
    maze[cur._row*N + cur._col] = 2;                    //将该节点赋值为2,表示来过
    for (int i = 0; i < 4; ++i) {                       //分别判断该节点的4个方向是否可走
        cur = entry;                                    //每次循环时,回复到初始节点
        cur = cur + test[i];                            //加上偏移量
        if (CheckIsAccess(maze, N, cur)) {              //判断该节点的该方向是否可走
            GetMazePath(maze, N, cur);                  //满足则进行递归调用,则可以遍历完所有的路径
        }
    }
}

//测试函数
void TestMaze() {
    int* maze = new int[100];  //利用指针声明一个一维数组,方便传参
    Pos entry = { 2,0 };       //设置迷宫入口点
    GetMaze(maze);         //获取地图内容

    PrintMaze(maze, 10);       //打印迷宫,此次为初始化时的迷宫
    GetMazePath(maze, 10, path, entry);//获取迷宫路径
    PrintMaze(maze, 10);       //打印迷宫,此为遍历完所有路的迷宫地图
}

//主函数
int main(){
    TestMaze(); //调用迷宫测试函数
    return 0;
}

运行结果:
《栈应用实例--迷宫问题》

使用栈实现迷宫

递归调用思路简单,编写起来也很容易,接下来我们将递归改成非递归,就需要用到栈。
同样是上面的那个地图,采用STL里面的栈,实现迷宫
思路与上面大体相同
使用上面的代码,则只需要修改GetMazePath()函数和TestMaze()函数
代码实现:

//获取地图路径
void GetMazePath(int* maze, size_t N, stack<Pos>& path, Pos entry) {
    assert(maze);
    Pos test[4] = { { -1,0 },{ 0,1 },{ 1,0 },{ 0,-1 } };//上,右,下,左
    Pos cur = entry;                                    //赋值当前节点为cur
    path.push(cur);                                     //将该节点压入栈中
    maze[cur._row*N + cur._col] = 2;                    //将该节点赋值为2,标记为走过
    while (!path.empty()) {                             //循环条件为栈是否为空
        int i = 0;
        for (i = 0; i < 4; ++i) {
            cur = path.top();                           //每次循环时,将cur的值复位为栈顶元素
            cur = cur + test[i];                        //cur改为新坐标
            if (CheckIsAccess(maze, N, cur)) {          //判断新坐标是否可走
                maze[cur._row*N + cur._col] = 2;        //如果可走,将该节点赋值为2
                path.push(cur);                         //并将该节点压入栈中
                break;                                  //如果可以通则break,如果此路最终不能达到终点,返回时在判断该坐标点的四周是否可走,即采用深度优先的思想走迷宫
            }
        }
        if (i == 4) {                                   //如果i为4,即说明该坐标的四周都无可走路,则弹栈
            path.pop();
        }
    }
}
//测试函数
void TestMaze() {
    stack<Pos> path;                         //声明一个路径栈
    int* maze = new int[100];                //声明一个数组
    Pos entry = { 2,0 };                     //设置初始入口点
    GetMaze(maze);                       //获取地图
    cout << "初始化地图" << endl;              
    PrintMaze(maze, 10);                     //打印初始化地图
    path.push(entry);                        //将入口压栈
    maze[entry._row * 10 + entry._col] = 2;  //将入口赋值为2,表示来过
    GetMazePath(maze, 10, path, entry);      //获取路径
    PrintMaze(maze, 10);                     //打印迷宫地图
}

运行结果:
《栈应用实例--迷宫问题》

使用栈查找迷宫最短路

上面讲解的方法只是遍历了所有路,如果需要返回是否找到通路,可以使用栈来保存路径所经过的坐标
当迷宫具有多条通路时,就需要返回最短路径,所以我们的地图也升级成为了下图
《栈应用实例--迷宫问题》
如图所示,在迷宫中有一条回路,即有多条路径可以成为通路
如果需要找到最短路径,不仅需要保存通路的路径,还要保证是最短的,这就需要使用两个栈,一个保存当前通路的路径,另一个保存最短路径
思路也有一点变化

  • 获取地图
  • 以起点开始,标记为2,此后每走一步,标记自加,存进数组
  • 判断是否为最短路

代码上还是只需要改动两个关键的函数即可

代码实现:

stack<Pos> check;    //声明一个全局栈,用来保存最短路径

//打印地图
void PrintMaze(int* maze, size_t N) {
    assert(maze);
    for (size_t i = 0; i < N; ++i) {
        for (size_t j = 0; j < N; ++j) {
            cout << setw(3) << maze[i*N + j]<<" ";    //使用setw()函数,使输出方便观察
        }
        cout << endl;
    }
}
//判断是否可走
bool CheckIsAccess(int* maze, size_t N, Pos pos,stack<Pos> path) {
    assert(maze);
    if ((pos._col >= 0) && (pos._col < N) && (pos._row >= 0) && (pos._row < N)) {    //判断是否越界
        if (maze[pos._row*N + pos._col] != 1) {                                      //判断是否为墙
            if (maze[pos._row*N + pos._col] == 0) {                                  //判断是否为未走过的路 
                return true;
            }
            if (( maze[pos._row*N + pos._col] - maze[path.top()._row*N + path.top()._col])>1) {
            //判断该节点与栈顶元素的差值是否大于1,如果为真,即说明该节点曾走过
                return true;
            }
        }
    }
    else {
        if(!(pos._row==2 && pos._col==-1))    
        //如果不是入口点的左节点,将path的元素赋给check
            check = path;
    }

    return false;    //如果不满足上述条件,即表明该节点四周都不能走
}
//获取地图路径
void GetMazePath(int* maze, size_t N, stack<Pos>& path, Pos entry,int js) {
    assert(maze);
    Pos test[4] = { {-1,0},{0,1},{1,0},{0,-1} };//上,右,下,左
    Pos cur = entry;
    path.push(cur);    //压栈
    maze[cur._row*N + cur._col] = js;    //当前节点的值
    for (int i = 0; i < 4; ++i) {
        cur = entry;
        cur = cur + test[i];
        if (CheckIsAccess(maze, N, cur,path)) {    //判断是否可以走
            GetMazePath(maze, N, path, cur, js + 1);    //每次递归调用时js+1
        }
    }
    path.pop();    //如果本节点的4个方向都探测完后,将此节点弹栈
}
//测试函数
void TestMaze() {
    stack<Pos> path;
    int* maze= new int[100];
    Pos entry = { 2,0 };
    GetMaze(maze);
    PrintMaze(maze, 10);
    path.push(entry);
    GetMazePath(maze, 10, path, entry, 2);    //初始的时候js赋值为2
    PrintMaze(maze, 10);
    path.pop();                               //最后将迷宫入口点弹栈
}

运行结果:
《栈应用实例--迷宫问题》
如运行结果所示,数字按照每条路依次递增,简单明了。
如果想知道具体路径可以查看check里面的值
《栈应用实例--迷宫问题》

这里就简单的实现了一下迷宫问题。

    原文作者:迷宫问题
    原文地址: https://blog.csdn.net/baidu_27435045/article/details/53437619
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞