问题描述:
函数流程图:
源代码:
#include<iostream>
#include<fstream>
#include <time.h>
#include <stdlib.h>
#include <iomanip>
#include <vector>
using namespace std;
struct Coor //定义描当前位置的结构类型
{
int row;
int column;
};
struct PointNode
{
int row;
int column;
int direction;
PointNode(int r, int c, int dir)
{
row = r;
column = c;
direction = dir;
}
};
struct LinkNode //链表结点 , 栈中存储的元素的类型就是LinkNode 的类型。
{
Coor data;
LinkNode* next;
};
//定义栈 虽然说stl库中有我们所需要的相应的数据结构,但我们还是直接再利用链表去封装一个栈的类型吧。
class stack
{
private:
LinkNode* top;
public:
stack(); //构造函数,置空栈
~stack(); //析构函数 对象的生命周期结束的时候,就会自动执行析构函数,从而实现我们的类对象的及时的删除,避免浪费空间。
void Push(Coor data); //把元素data压入栈中
Coor Pop(); //使栈顶元素出栈
Coor GetPop(); //只获得栈顶元素的data,栈顶元素并不会出栈。
void Clear(); //把栈清空
bool IsEmpty(); //判断栈是否为空
};
// 栈类的一系列的经典操作********
stack::stack() //构造函数,置空栈
{
top = NULL;
}
stack::~stack() {}
void stack::Push(Coor x) //把元素data压入栈中
{
LinkNode* TempNode;
TempNode = new LinkNode; // 注意这里要用我们的new去申请空间,因为后面我们要用delete 去删除我们的出栈的节点。
TempNode->data = x;
TempNode->next = top;
top = TempNode;
}
Coor stack::Pop() //使栈顶元素出栈
{
Coor Temp;
LinkNode* TempNode;
TempNode = top;
top = top->next;
Temp = TempNode->data;
delete TempNode;
return Temp;
}
Coor stack::GetPop() //直接获取栈顶元素的data值,并不删除栈顶元素。
{
return top->data;
}
void stack::Clear() //把栈清空 , 在出栈的同时我们要释放相应的节点的空间。
{
while (top)
{
LinkNode* Temp;
Temp = top;
delete Temp;
top = top->next;
}
top = NULL; // 最后将栈指针置空 。
}
bool stack::IsEmpty()
{
if (top == NULL) return true;
return false;
}
// *********************** \\
//定义移动的偏移量
int dx[4] = { 0 , 1 , 0 , -1 }; // x 方向的偏移量。
int dy[4] = { 1 , 0 , -1 , 0 }; // y 方向的偏移量。
vector<PointNode> Point; // 存储点的容器
int x_present, y_present; // 标记我们当前走到的位置。
// ***********函数的声明 *********** //
int** GetMaze(int& m, int& n); // 生成迷宫
bool bfs_Mazepath(int** maze, int m, int n); // 深搜求路径
bool dfs_Mazepath(int** maze, int m, int n); // 宽搜求路径。
void PrintPath(stack p, int** maze, int m, int n); //输出宽搜路径。
void PrintPath(int** maze, int m, int n); //输出深搜路径。
void print(int* row, int n, int m, int p); // 是输出在GetMaze()函数中生成的初始迷宫的辅助函数。
void backUp(int** maze, int** backup, int m, int n); // 恢复迷宫,便于不同搜索方式的执行。
void menu(); // 对我们的主菜单进行简单的渲染;
void Search_present(int** maze , int m , int n ); // 深搜算法的辅助函数。
int** getInvaildMaze(int m, int n);
int main() ; // 主函数,程序执行的开始
//*************函数的声明***********//
// 各类函数的定义(按照声明的顺序来)
int** GetMaze(int& m, int& n) // 注意这里是引用,我们还要根据引用去修改m , n 的值。
{
int** maze;
int i = 0, j = 0;
char Choose;
cout << "请输入你想生成的迷宫的行数和列数: ";
cin >> m >> n;
while (m <= 0 || n <= 0)
{
system("color E4");
cout << "(wc)哥哥莫不是在消遣我? , 再让你输入一次: ";
cin >> m >> n;
}
system("color E0");
//定义一个标志,选择读取文件或直接输入,获取迷宫
cout << "请选择:随机生成(1)或 (如果不嫌累的话)手动键盘输入(2):\n";
cin >> Choose;
while ((Choose != '1') && (Choose != '2'))
{
cout << "请睁大你的眼睛仔细看看我让你输入的什么东西!,再让你输一次嗷:";
cin >> Choose;
}
if (Choose == '1') //当标志Choose为‘1’时,读取文件
{
while ((m % 2 == 0 ) || (n % 2 == 0 ))
{
cout << "如果你想要生成随机的迷宫就请你输入的行和列都是奇数:";
cin >> m >> n;
}
//cout << "hello world " << endl ;
maze = getInvaildMaze(m, n);
}
else if (Choose == '2') //Choose=2 ,手动输入迷宫
{
cout << "请输入迷宫内容:(0表示通路,1表示不连通。中间用空格键分开)\n";
maze = new int* [m + 2]; //申请长度等于行数加2的二级指针
for (i = 0; i < m + 2; i++) //申请每个二维指针的空间
{
maze[i] = new int[n + 2];
}
for (i = 1; i <= m; i++)
{
for (j = 1; j <= n; j++)
{
cin >> maze[i][j];
}
}
}
//给迷宫的四周加一堵墙,即把迷宫四周定义为1
for (i = 0; i < m + 2; i++) // 给左右加上墙。
maze[i][0] = maze[i][n + 1] = 1;
for (i = 0; i < n + 2; i++) maze[0][i] = maze[m + 1][i] = 1; // 给上下加上墙。
cout << endl;
cout << "我们生成了如下图的迷宫:\n";
for (int p = 0; p < m + 2; ++p) // 根据相应位置的是0 或是 1 去实现这个迷宫的可视化。
{
print(maze[p], n, m, p);
}
return maze; //返回存贮迷宫的二维指针maze
}
bool bfs_Mazepath(int** maze, int m, int n) // 寻找路径的函数。, 宽搜版本 ,
{
stack q, p; //定义栈p、q,分别存探索迷宫的存储和路径过程
Coor Temp1, Temp2;
int row, column, loop;
Temp1.row = 1;
Temp1.column = 1;
q.Push(Temp1); //将入口位置入栈
p.Push(Temp1); // 第一个节点进入我们的存储栈中。
maze[1][1] = -1; //标志入口位置已到达过 , 防止在搜索的过程中搜回已经搜过的点,从而造成栈的死循环。
while (!q.IsEmpty()) // 路径栈不为空的话,我们就继续输出。
{
Temp2 = q.GetPop(); //获取栈顶元素的 data的值 。
if (!((p.GetPop().row == q.GetPop().row) && (p.GetPop().column == q.GetPop().column))) // 如果路径栈的栈顶元素跟存储栈的栈顶元素不是同一个元素
// 我们就将路径栈上的元素在存储栈上入栈。
p.Push(Temp2);
for (loop = 0; loop < 4; loop++) //探索当前位置的4个相邻位置
{
row = Temp2.row + dx[loop]; //计算出新位置 x位置值
column = Temp2.column + dy[loop]; //计算出新位置 y 位置值
if (maze[row][column] == 0) //判断新位置是否可达 , 同时也防止了我们“回头”。
{
Temp1.row = row;
Temp1.column = column;
maze[row][column] = -1; //标志新位置已到达过,防止我们再反过来重新搜到从而造成死循环,
q.Push(Temp1); // 将这个节点加入到我们的路径栈中,
}
if ((row == m) && (column == n)) //说明路径栈的当前顶点的一个邻点是我们的终点,从而说明到达了出口。
{
//cout << "hello world" << endl ;
Temp1.row = m;
Temp1.column = n;
p.Push(Temp1); //把最后一个位置入栈
PrintPath(p, maze, m, n); //矩阵显示输出
return true; //表示成功找到路径
}
}
if (p.GetPop().row == q.GetPop().row && p.GetPop().column == q.GetPop().column) // 这个判断是当我们走到一个死路的时候存储栈的回退。
//如果没有新位置入栈,则返回到上一个位置
{
p.Pop();
q.Pop();
// maze[data.row][data.column] = 9 ; // 标志这个位置是一个无效位置,(这里不重新赋值为零是为了防止入栈的死循环)。
}
}
return false; //表示查找失败,即迷宫无路经
}
bool dfs_Mazepath(int** maze, int row, int column, int m, int n) //
{
if (row == m && column == n)
{
maze[m][n] = 9;
return true;
}
maze[row][column] = 9;
for (int i = 0; i < 4; i++)
{
int a, b;
a = row + dx[i];
b = column + dy[i];
if (maze[a][b] == 0)
{
if (dfs_Mazepath(maze, a, b, m, n))
return true;
}
}
// 恢复现场
maze[row][column] = 0;
return false;
}
void PrintPath(stack p, int** maze, int m, int n) //输出路径
{
cout << "迷宫的路径为下图中'㊣' 所标记的路径\n";
int a, b;
Coor data;
LinkNode* Temp;
while (!p.IsEmpty())
{
data = p.Pop();
maze[data.row][data.column] = 10;
}
//************调试代码***********
// for(int i = 1 ; i <= m ; i ++ )
// {
// for(int j = 1 ; j <= m ; j ++ )
// {
// cout << maze[i][j] << ' ' ;
// }
// cout << endl ;
// }
//********************************
for (int i = 0; i <= m + 1; i++)
{
cout << " ";
for (int j = 0; j <= n + 1; j++)
{
if (i == 0 || i == m + 1 || j == 0 || j == n + 1)
{
cout << "■";
}
else if (maze[i][j] == 0 || maze[i][j] == -1)
{
cout << " ";
}
else if (maze[i][j] == 1)
{
cout << "■";
}
else if (maze[i][j] == 10)
{
cout << "㊣";
}
}
cout << endl;
}
return;
}
void PrintPath(int** maze, int m, int n)
{
for (int i = 0; i <= m + 1; i++)
{
cout << " ";
for (int j = 0; j <= n + 1; j++)
{
if (i == 0 || i == m + 1 || j == 0 || j == n + 1)
{
cout << "■";
}
else if (maze[i][j] == 0 || maze[i][j] == -1)
{
cout << " ";
}
else if (maze[i][j] == 1)
{
cout << "■";
}
else if (maze[i][j] == 9)
{
cout << "㊣";
}
}
cout << endl;
}
return;
}
void print(int* row, int n, int m, int p)
{
cout << " ";
for (int i = 0; i <= n + 1; i++)
{
if (p == 0 || p == m + 1)
cout << "■";
else if (i == 0 || i == n + 1)
cout << "■"; // ★
else if (row[i] == 0)
cout << " "; // ㊣
else if (row[i] == 1)
cout << "■";
}
cout << endl;
}
void backUp(int** maze, int** backup, int m, int n)
{
for (int i = 0; i < m + 2; i++)
{
for (int j = 0; j < n + 2; j++)
{
backup[i][j] = maze[i][j];
}
}
}
void menu()
{
system("color E0");
cout << setiosflags(ios::right | ios::showpoint);
cout.width(100); // 70
cout << "欢迎来到迷宫小程序" << endl;
cout << endl << endl;
cout.width(95);
cout << "迷宫说明" << endl;
cout.width(85);
cout << "障碍物: ■" << " " << "无障碍物的小格子:空格" << endl;
cout << endl;
cout.width(85);
cout << " 外围墙体:■" << " " << "描述一条路径 " << "㊣" << endl;
}
void search_present(int** maze , int m , int n )
{
// 右 , 下 , 左 , 上 ;
int row, column;
for (int i = 0; i < 4; i++) // 0 , 1 , 2 , 3 分别代表的方向是: 右 下 左 上
{
row = x_present + dx[i];
column = y_present + dy[i];
if (row >= 1 && row <= m && column >= 1 && column <= n && maze[row][column] == 1)
{
PointNode t(row, column, i);
Point.push_back( t );
}
}
}
int** getInvaildMaze(int m, int n) // 随机生成一个m行n列的最少具有一条通路的迷宫,
// 并且返回这个迷宫矩阵的首地址。(利用普利姆算法实现迷宫的生成)
{
//随机生成的迷宫的行和列必须是奇数,
//开辟迷宫空间。
// 1 为墙 0 为可以走的通路。
int** maze;
maze = new int* [m + 2];
for (int i = 0; i < m + 2; i++)
{
maze[i] = new int[n + 2];
}
srand(time(0)); // 随机数种子。
// 将所有位置上的都设置为墙。
for (int i = 0; i <= m + 1; i++)
{
for (int j = 0; j <= n + 1; j++)
{
maze[i][j] = 1; // 将所有的位置上的空格都设置为障碍物。
}
}
Point.clear();
// 下面对我们的迷宫进行实现。
x_present = y_present = 1; // 入口点。
maze[x_present][y_present] = 0 ;
search_present(maze , m , n );
// 0 , 1 , 2 , 3 分别代表的方向是: 右 下 左 上
// cout << "hello world " << endl ;
while (Point.size())
{
int wall_sum = Point.size();
int Randnum;
Randnum = rand() % wall_sum;
PointNode new_wall = Point[Randnum];
x_present = new_wall.row;
y_present = new_wall.column;
int t = new_wall.direction;
if (t == 0) // 向右走 , x 不变, y ++ ;
{
y_present++;
}
else if (t == 1)
{
x_present++;
}
else if (t == 2)
{
y_present--;
}
else {
x_present--;
}
// 现在走到墙的后面的一格。
// Point数组中的墙一定是迷宫范围内的墙,不会是外围的墙。
int& x = x_present;
int& y = y_present;
if (x >= 1 && y >= 1 && x <= m && y <= n && maze[x][y] == 1)
{
maze[new_wall.row][new_wall.column] = maze[x][y] = 0;
search_present(maze , m , n ) ; // 搜索当前位置四周的墙加入我们的容器中。
}
Point.erase(Point.begin() + Randnum);
}
// for(int i = 1 ; i <= m ; i ++ )
// {
// for(int j = 1 ; j <= n ; j ++ )
// {
// cout << maze[i][j] << ' ' ;
// }
// cout << endl;
// }
return maze ;
}
int main()
{
menu();
int m = 0, n = 0;
int** maze; // 定义二级指针,从而存储我们的迷宫矩阵的首地址。
int** backup; // 备份一下,一会便于恢复。
maze = GetMaze(m, n); //调用GetMaze(int &m,int &n)函数,得到迷宫矩阵的首地址 , 同时注意参数的调用是引用。, m , n 的值被修改了!
backup = new int* [m + 2];
for (int i = 0; i < m + 2; i++)
{
backup[i] = new int[n + 2];
}
backUp(maze, backup, m, n); // 将第一个参数的copy给第二个参数
cout << "请问你想使用深搜( D/d )来走迷宫;还是想用宽搜来走迷宫( B/b ) (tips:输入0退出我们的选择): ";
char choose;
cin >> choose;
while (choose != '0')
{
while (choose != 'D' && choose != 'd' && choose != 'B' && choose != 'b' && choose != '0')
{
cout << "请再仔细看看我让你输入的东西是什么,再让你输一次: ";
cin >> choose;
}
if (choose == '0') return 0;
if (choose == 'D' || choose == 'd')
{
if (dfs_Mazepath(maze, 1, 1, m, n))
{
PrintPath(maze, m, n);
cout << "迷宫路径探索成功!\n";
}
else cout << "NO Path \n";
backUp(backup, maze, m, n);
}
else if (choose == 'B' || choose == 'b')
{
if (bfs_Mazepath(maze, m, n)) //调用Mazepath(int **maze,int m,int n)函数获取路径
cout << "迷宫路径探索成功!\n";
else cout << "NO Path \n";
backUp(backup, maze, m, n);
}
cout << "请问你想使用深搜( D/d )来走迷宫;还是想用宽搜来走迷宫( B/b ) (tips:输入0退出我们的选择): ";
cin >> choose;
}
return 0;
}