栈应用实例--迷宫问题

来源:互联网 发布:h5切水果游戏源码 编辑:程序博客网 时间:2024/05/21 17:48

调试环境: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里面的值
check值

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

1 0
原创粉丝点击