题目要求
深度遍历搜索迷宫路径
输入要求:
1、请输入迷宫的行列数(例如:10 10)
2、请输入迷宫的路径信息(0表示可以走,1表示不能走)
3、输入示例:
0 0 0 1 1
1 0 0 0 1
1 1 0 1 1
1 1 0 0 1
1 1 1 0 0
输出要求:
1、如果没有路径,直接输出 : 不存在一条迷宫路径!
2、如果有路径,直接输出 : 将路径上的0改为*号然后输出!
* * * 1 1
1 0 * 0 1
1 1 * 1 1
1 1 * * 1
1 1 1 * *
深度遍历迷宫解题思路
- 首先大家需要了解深度遍历的思想、即在遍历过程中、总是在一个方向上一直找、直到不能在往下走、然后在切换方向、因此我们需要事先规定好寻找方向的顺序(此处给出的方向是 右->下->左->上),那么在遍历每个节点时、先判断当前节点的右方向是否能走、如果能走就直接进入当前节点的右边节点、否则才去判断当前节点的下边是否能走、以此类推,直到找到路径或者没有路径
- 因为是深度遍历、因此借助递归的思想、此处我们C++STL容器适配器的栈、帮助完成深度遍历
- 我们先将判断入口节点(左上角第一个节点)、如果入口节点是1、那就说明迷宫没有入口、直接返回并打印没有路径,否则就先将第一个节点入栈、然后取出栈顶元素(此时的栈顶元素就是第一个元素),按照上述方式判断当前节点的下一个节点是否位0,也就是能不能走,(注意此处判断的顺序),如果能走就将下一个节点入栈,如果不能走,就将当前节点出栈、最后栈中元素就是路径,当然前提是栈不为空,如果栈为空,就是没有找到路径
- 另外需要注意的一个点就是找过的节点就不再能在找、否则会出现环
注意:我们在这里需要对每个节点进行简单的封装,首先我们每个节点肯定必须有自己的横纵坐标,自己的状态(0或者1),还有一个重要的信息需要写入每个节点中,就是节点四个方向上的节点的状态(0或者1),这里单独将节点信息列出来
节点代码
const int DIR_SIZE = 4;
struct Node
{
int nx; // 横坐标
int ny; // 纵坐标
int nval; // 状态值
bool nstats[DIR_SIZE]; // 四个方向的状态值
};
迷宫代码
#include <iostream>
#include <stack> // STL容器适配器的stack
using namespace std;
//表示方向、注意四个值刚好对应节点中保存四个方向状态信息的下标
const int RIGHT = 0;
const int DOWN = 1;
const int LEFT = 2;
const int UP = 3;
// 表示存储方向数组的大小(四个方向)
const int DIR_SIZE = 4;
// 表示状态信息
const bool YES = true; // 表示可以走
const bool NO = false; // 表示不能走
// 迷宫类
class Maze
{
private:
// 需要迷宫每个节点的类型、此处因此每个节点都有自己的x、y坐标、自己的状态(0,1)
// 以及自己四个方向上的是否能走的、因此使用结构体来封装节点
struct Node
{
int nx; // 横坐标
int ny; // 纵坐标
int nval; // 状态值
bool nstats[DIR_SIZE]; // 四个方向是否能走
};
Node** marr; // 迷宫相当于是个二维表
int mrow; // 迷宫的行
int mcol; // 迷宫的列
stack<Node> mstack; // 深度遍历迷宫使用栈存储迷宫中最终找到的路径节点
public:
Maze(int row, int col)
: mrow(row)
, mcol(col)
{
// 动态开辟二维数组
marr = new Node*[row];
for (int i = 0; i < row; ++i)
{
marr[i] = new Node[col];
}
}
~Maze()
{
for (int i = 0; i < mrow; ++i)
{
delete[]marr[i];
}
delete[]marr;
marr = nullptr;
}
// 初始化每个节点的状态信息、并将节点四个方向上都先设置为不能走、
void init_maze(int x, int y, int val)
{
marr[x][y].nx = x;
marr[x][y].ny = y;
marr[x][y].nval = val;
for (int i = 0; i < DIR_SIZE; ++i)
{
marr[x][y].nstats[i] = NO;
}
}
// 在初始化完成以后每个节点都知道自己四个方向是否能走、并将其设置到节点的方向数组中
void set_dir_stats()
{
for (int i = 0; i < mrow; ++i)
{
for (int j = 0; j < mcol; ++j)
{
// j < mcol -1 防止最右边一列元素访问越界
if (j < mcol - 1 && marr[i][j + 1].nval == 0)
{
marr[i][j].nstats[RIGHT] = YES;
}
// i < mrow -1 防止最下边一行元素访问越界
if (i < mrow - 1 && marr[i + 1][j].nval == 0)
{
marr[i][j].nstats[DOWN] = YES;
}
// j > 0 防止最左边一列元素访问越界
if (j > 0 && marr[i][j - 1].nval == 0)
{
marr[i][j].nstats[LEFT] = YES;
}
// i > 0 防止最上边一行元素访问越界
if (i > 0 && marr[i - 1][j].nval == 0)
{
marr[i][j].nstats[UP] = YES;
}
}
}
}
// 深度遍历迷宫、寻找路径
void traverse_maze_find_path()
{
// 表示第一个节点都不能进入、即该迷宫没有入口
if (marr[0][0].nval == 1)
{
return;
}
// 否则就是有入口、先将第一个节点放入栈中
mstack.push(marr[0][0]);
while (!mstack.empty())
{
// 每次拿出栈顶元素进行判断
int x = mstack.top().nx;
int y = mstack.top().ny;
// 表示已经找到最后一个元素了
if (x == mrow - 1 && y == mcol - 1)
{
return;
}
// 此处使用判断顺序是右 -》 下 -》 左 -》 上
// 右
if (marr[x][y].nstats[RIGHT] == YES)
{
// 首先设置限制、防止死循环
// 如果走到下一个节点后发现这个节点是个死胡同、然后跳回前一个节点
// 再次判断时就不再走这个节点了、而是往其他方向寻找出路
// 如果不进行设置、就会在这两个节点上来回跳跃、形成死循环
marr[x][y].nstats[RIGHT] = NO;
marr[x][y + 1].nstats[LEFT] = NO;
mstack.push(marr[x][y + 1]);
continue;
}
// 下
if (marr[x][y].nstats[DOWN] == YES)
{
marr[x][y].nstats[DOWN] = NO;
marr[x + 1][y].nstats[UP] = NO;
mstack.push(marr[x + 1][y]);
continue;
}
// 左
if (marr[x][y].nstats[LEFT] == YES)
{
marr[x][y].nstats[LEFT] = NO;
marr[x][y - 1].nstats[RIGHT] = NO;
mstack.push(marr[x][y - 1]);
continue;
}
// 上
if (marr[x][y].nstats[UP] == YES)
{
marr[x][y].nstats[UP] = NO;
marr[x - 1][y].nstats[DOWN] = NO;
mstack.push(marr[x - 1][y]);
continue;
}
// 如果当前节点的四个方向都不能左、就让当前节点出栈(栈顶元素)
mstack.pop();
}
}
// 输出深度遍历结果
void show_maze_path()
{
if (mstack.empty())
{
cout << "不存在一条迷宫路径!" << endl;
return;
}
// 将路径上的元素设置为*
while (!mstack.empty())
{
Node cur = mstack.top();
marr[cur.nx][cur.ny].nval = '*';
mstack.pop();
}
for (int i = 0; i < mrow; ++i)
{
for (int j = 0; j < mcol; ++j)
{
if (marr[i][j].nval == '*')
{
cout << "*" << " ";
}
else
{
cout << marr[i][j].nval << " ";
}
}
cout << endl;
}
}
};
int main()
{
int row;
int col;
cout << "请输入迷宫的行列数(例如:10 10): " << endl;
cin >> row >> col;
// 初始话一个迷宫对象
Maze maze(row, col);
int data = 0;
cout << "请输入迷宫的路径信息(0表示可以走,1表示不能走): " << endl;
for (int i = 0; i < row; ++i)
{
for (int j = 0; j < col; ++j)
{
cin >> data;
maze.init_maze(i, j, data);
}
}
// 设置每个方向上能否走的状态
maze.set_dir_stats();
// 深度遍历迷宫寻找路径
maze.traverse_maze_find_path();
// 输出深度遍历结果
maze.show_maze_path();
return 0;
}
总结:大家一定要细心体会,如果有不懂可以多画一画图,有助于自己理解!