栈和递归解决迷宫问题

先给出一个迷宫的模型。0代表通路,1代表墙壁,知道出入口后,找到入口到出口之间的通路。
先给出一个简单迷宫模型
栈和递归解决迷宫问题_第1张图片
现在令下方为出口方向,[2,0]作为入口寻找通路。将走过的路程标记为2。
此时可以发现迷宫存在一个分叉口,假如以顺时针方向对是否存在通路进行判别,则必定会先走没有出口的那一条死路,而当走进死路时,需要进行回溯。
此时可以开辟一个栈用来维护这条通路,先将入口进行压栈,随后判别通路,将通路压栈,当走到死路的时候,进行一次出栈操作。当回溯到有其他通路时,则进行压栈,保存下一条通路了。
代码实现:
利用递归保证对每一条通路都会进行判别。

#pragma once
#include 
#include 
#include 
#include 
using namespace std;

struct Pos    //定义一个坐标
{
    size_t _row;    //行
    size_t _col;    //列
};
//回溯
//利用栈来记录以及回溯 pop
#if 1
template 
class Maze
{
public:
    Maze(int maze[][N]);    //初始化一个迷宫
    void Print();   //打印迷宫
    bool CheckAccess(Pos next);  //判断该点是否合法
    bool GetPath(Pos entry);
protected:
    int _maze[M][N];
};
template 
Maze::Maze(int maze[][N]) {   //初始化迷宫
    for (size_t i = 0; i < M; i++) {
        for (size_t j = 0; j < N; j++) {
            _maze[i][j] = maze[i][j];
        }
    }
}
template 
void Maze::Print() {          //打印迷宫
    for (size_t i = 0; i < M; i++) {
        for (size_t j = 0; j < N; j++) {
            cout << _maze[i][j] << " ";
        }
        cout << endl;
    }
    cout << endl;
}
template 
bool Maze::CheckAccess(Pos next) {   //判断该点是否合法
                                           //不溢出或者有通路返回真
    if (next._row < M&&next._col < N&&_maze[next._row][next._col] == 0)
        return true;
    return false;
}
template 
bool Maze::GetPath(Pos entry) {    //找出口
    Pos cur;
    cur = entry;
    Pos next;

    stackpath;    //创建栈帧
    path.push(entry);  //将起点压栈
    while (!path.empty())   //当栈为空证明没有出口 跳出
    {
        cur = path.top();   //起始即为起点
        //每次都是栈顶的点
        _maze[cur._row][cur._col] = 2;
        if (cur._row == M - 1) {
            return true;
        }
        //顺时针排查
        next = cur;        //next指向当前位置
        next._row -= 1;    //上
        if (CheckAccess(next)) {   //检验上方是否有通路
            path.push(next);  //如果符合则压栈
            continue;
        }
        next = cur;        //next指向当前位置
        next._col += 1;    //右
        if (CheckAccess(next)) {   //检验右方是否有通路
            path.push(next);  //如果符合则压栈
            continue;
        }
        next = cur;        //next指向当前位置
        next._row += 1;    //下
        if (CheckAccess(next)) {   //检验下方是否有通路
            path.push(next);  //如果符合则压
            continue;
        }
        next = cur;        //next指向当前位置
        next._col -= 1;    //左
        if (CheckAccess(next)) {   //检验左方是否有通路
            path.push(next);  //如果符合则压
            continue;
        }
        path.pop();  //如果以上4个都不符合 证明走到死路  利用后进先出的特点进行回溯
    }
    return false;
}
void test() {
    int mazeA[10][10] = {
        { 1,1,1,1,1,1,1,1,1,1 },
        { 1,1,1,1,1,1,1,1,1,1 },
        { 0,0,0,1,1,1,1,1,1,1 },
        { 1,1,0,1,1,1,1,1,1,1 },
        { 1,1,0,1,1,1,1,1,1,1 },
        { 1,1,0,1,1,1,1,1,1,1 },
        { 1,1,0,0,0,1,1,1,1,1 },
        { 1,1,0,1,1,1,1,1,1,1 },
        { 1,1,0,1,1,1,1,1,1,1 },
        { 1,1,0,1,1,1,1,1,1,1 }
    };
    Maze<10, 10>maze(mazeA);
    maze.Print();
    Pos entry = { 2,0 };
    maze.GetPath(entry);
    maze.Print();
}

期待的运行方式:
栈和递归解决迷宫问题_第2张图片
运行结果:
栈和递归解决迷宫问题_第3张图片
与所期待的运行结果相同。
缺点:不能对有两条通路以上的迷宫求最优解,也不能对带环的迷宫进行求解。

解决求解有两条以上通路以及带环的迷宫的最短路;
对于带有两条以上通路的迷宫:
依然使用栈进行压栈,出栈(回溯)操作,此时在程序内另外开辟一个shortPath用来保存最短路程。path用来保存每一次的通路路程。每找到一条通路就用path和shortPath比较,如果path比shortPath更短,则将path赋值给shortPath。
栈和递归解决迷宫问题_第4张图片
其中蓝色路线是shortPath中所保存的最短路程。
对于带有环的迷宫:
栈和递归解决迷宫问题_第5张图片
如果用之前的代码会发现,程序保存的最短路程将会是。
栈和递归解决迷宫问题_第6张图片
并不是我们所期待的最短路了。
此时之前的2标记法已经不再够用,通路的判别方式也需要改变。
1.现在让入口的标记为2,之后每一个通路点都是前一个点+1。
2.判断通路的条件改为

if (((next._row < M&&next._col < N)&&(_maze[next._row][next._col] == 0))||(_maze[cur._row][cur._col]<_maze[next._row][next._col]))
//不越界&&有通路 || 下一个点的坐标值大于当前点的坐标值

此时的通路应该为:
栈和递归解决迷宫问题_第7张图片
程序走过的路程为:
栈和递归解决迷宫问题_第8张图片
这边是迷宫的最终版本
实现:

template 
class Maze
{
public:
    Maze(int maze[][N]);    //初始化一个迷宫
    void Print();   //打印迷宫
    bool CheckAccess(Pos cur,Pos next) {   //判断该点是否合法
                                   //不溢出或者有通路返回真
        if (((next._row < M&&next._col < N)&&(_maze[next._row][next._col] == 0))||(_maze[cur._row][cur._col]<_maze[next._row][next._col]))
            return true;
        return false;
    }
    void GetPath(Pos entry, stack&path) {    //找出口   //传入一个path用来记录路程
        Pos cur = entry;

        path.push(entry);  //将起点压栈
        if (cur._row == M - 1) {
            if (shortPath.empty() || shortPath.size() > path.size()) {   //如果最短路程为空或大于则赋值
                shortPath = path;
            }
            path.pop();
            return;
        }
        //顺时针排查

        Pos next;

        next = entry;  //next指向当前位置
        next._row -= 1;    //上
        if (CheckAccess(cur,next)) {   //检验上方是否有通路
            _maze[next._row][next._col] = _maze[entry._row][entry._col] + 1;
            GetPath(next, path);//如果符合则压栈
        }

        next = entry;        //next指向当前位置
        next._col += 1;    //右
        if (CheckAccess(cur,next)) {   //检验右方是否有通路
            _maze[next._row][next._col] = _maze[entry._row][entry._col] + 1;
            GetPath(next, path);//如果符合则压栈
        }

        next = entry;        //next指向当前位置
        next._row += 1;    //下
        if (CheckAccess(cur,next)) {   //检验下方是否有通路
            _maze[next._row][next._col] = _maze[entry._row][entry._col] + 1;
            GetPath(next, path);//如果符合则压栈
        }

        next = entry;        //next指向当前位置
        next._col -= 1;    //左
        if (CheckAccess(cur,next)) {   //检验左方是否有通路
            _maze[next._row][next._col] = _maze[entry._row][entry._col] + 1;
            GetPath(next, path);//如果符合则压栈
        }
        path.pop();  //如果以上4个都不符合 证明走到死路  利用后进先出的特点进行回溯
    }
protected:
    int _maze[M][N];
    stack shortPath;   //创建一个栈对象 记录最短行程
};

template 
Maze::Maze(int maze[][N]) {   //初始化迷宫
    for (size_t i = 0; i < M; i++) {
        for (size_t j = 0; j < N; j++) {
            _maze[i][j] = maze[i][j];
        }
    }
}
template 
void Maze::Print() {          //打印迷宫
    for (size_t i = 0; i < M; i++) {
        for (size_t j = 0; j < N; j++) {
            cout << _maze[i][j] << "  ";
        }
        cout << endl;
    }
    cout << endl;
}
void test() {
    int mazeA[10][10] = {
        { 1,1,1,1,1,1,1,1,1,1 },
        { 1,1,1,1,1,1,1,1,1,1 },
        { 2,0,0,1,1,1,1,1,1,1 },  //令入口直接为2更方便
        { 1,1,0,1,1,1,1,1,1,1 },
        { 1,1,0,0,0,1,1,1,1,1 },
        { 1,1,0,1,0,1,1,1,1,1 },
        { 1,1,0,0,0,1,1,1,1,1 },
        { 1,1,0,1,1,1,1,1,1,1 },
        { 1,1,0,1,1,1,1,1,1,1 },
        { 1,1,0,1,1,1,1,1,1,1 }
    };
    Maze<10, 10>maze(mazeA);
    maze.Print();
    Pos entry = { 2,0 };
    stackpath;
    maze.GetPath(entry, path);
    maze.Print();
}

你可能感兴趣的:(c++,数据结构)