找了网上很多关于迷宫的代码,要么就是只有生成随机迷宫的,要么就是已经有了固定迷宫的数组找路径,找不到两者结合在一起的代码,没办法只能靠自己了。
言归正传生成随机迷宫需要用到查并集思想。
该设计共包含如下三个部分:
1构建随机迷宫
应用不相交集合构建迷宫的算法简要描述如下: 给定一个NxN 的方格(cells),初始时每个方格的四面都是墙(walls),如图6-58 (a) 所示,其中的s 是迷宫的开始处,F 是迷宫的结束处。NXN 迷宫的N 个方格0,1,..,N-1初始时每个方格自己成为一个等价类,即{0},{1},.,{N-1}。生成随机迷宫的方法是先建立一个vector动态数组,将除去上边框和左边框的剩下所有可以除去的墙编号(相当于每个单元的下边墙和右边墙,例如第一个单元的下边墙编号为1,右边墙为2,以此类推)放入这个数组中,在数组中随机选择一个内部墙(连接两个相邻方格的墙),如果该内部墙关联的两个相邻的方格属于不同的等价类就将该墙除去,在除去该墙的同时将这两个等价类合并。直到所有的方格都在一个等价类中,就完成了随机迷宫的生成
2寻找迷宫路径
搜索路径需要用到深度搜索和非递归用法的栈。迷宫一旦建立后,方格作为图中的顶点,如果两个相邻的方格之间没有墙则两个顶点之间有边。为找到从起点到终点的一条唯一的路径,在该图上从起点处开始出发进行深度搜索,如果有边则进入下一单元将这个单元的bianli属性标记为false,防止搜索返回进入死循环,并将这个单元的下标压栈。如果遇到死胡同就将这个下标出栈,回溯。知道找到终点则结束
3打印路径
深度搜索完毕则栈中存放了路径所经过单元的下标,我们用vector数组将栈中的数据取出,再利用windows.h库,设置光标属性并通过简单的数学计算在控制台输出相应单元对应的位置,则在地图中将路径打出
查并集类
#DisjSets.h
#include <vector>
#include <iostream>
#include<math.h>
#include<stack>
#include<vector>
#include<Windows.h>
using namespace std;
class DisjSets
{
public:
explicit DisjSets(int numElements);
stack<int>mes;
int find(int x);
void unionSets(int root1, int root2);
void setRightFalse(int x);
void setBottomFalse(int x);
bool getRight(int i)
{
return s[i].right;
}
bool getBottom(int i)
{
return s[i].bottom;
}
void get(int n,int m);
void xiansi(int m);
private:
struct cell
{
int element;
bool bottom; //下方的墙,true则代表有墙
bool right; //右方的墙,true则代表有墙
bool bianli; //方便后面DFS是记录是否遍历过
explicit cell(int ele = -1, bool bot = true, bool rg = true,bool bl=true) :
element(ele), bottom(bot), right(rg),bianli(bl){}
};
std::vector<cell> s;
vector<int>ss;
};
#DisjSets.cpp
#include "DisjSets.h"
DisjSets::DisjSets(int numElements) :s(numElements)
{
for (auto x : s)
{
x.element = -1;
x.bottom = true;
x.right = true;
x.bianli = true;
}
}
//root1和root2为互异的两个根
void DisjSets::unionSets(int root1, int root2)
{
if (s[root1].element<s[root2].element)//root1高度较高,将root2并到root1上
{
s[root2].element = root1;
}
else if (s[root1].element == s[root2].element)
{
s[root2].element = root1;
--s[root1].element;
}
else
{
s[root1].element = root2;
}
}
int DisjSets::find(int x)
{
if (s[x].element<0)
return x;
else
{
return s[x].element = find(s[x].element);
}
}
void DisjSets::setRightFalse(int x)
{
s[x].right = false;
}
void DisjSets::setBottomFalse(int x)
{
s[x].bottom = false;
}
void DisjSets::get(int n,int m)
{
int next[4] = { 1, -1, 4, -4 };
s[0].bianli = false;
int jilu = 1;
cout << endl;
cout << "深度搜索经过的全部点包括回溯的" << endl;
while (n != (m*m-1))
{
cout << mes.top()<<" ";
if (n<0 || n>(m*m-1))
continue;
else if (n == 0)
{
if (s[n + 1].bianli&&!s[n].right)
{
n++; s[n].bianli = false; mes.push(n);
}
else if (s[n + m].bianli&&!s[n].bottom)
{
n += m; s[n].bianli = false; mes.push(n);
}
}
else if (n % m!= 0 && s[n - 1].bianli && !s[n - 1].right)
{
n--; s[n].bianli = false; mes.push(n);
}
else if ((n+1) % m != 0 && s[n + 1].bianli&&!s[n].right)
{
n++; s[n].bianli = false; mes.push(n);
}
else if (n > (m-1) && s[n - m].bianli&&!s[n - m].bottom)
{
n -= m; s[n].bianli = false; mes.push(n);
}
else if (n <(m*m-m) && s[n + m].bianli&&!s[n].bottom)
{
n += m; s[n].bianli = false; mes.push(n);
}
else
{
mes.pop();
n = mes.top();
}
}
}
void DisjSets::xiansi(int m)
{
cout << endl;
cout << endl;
cout << "深度搜索所需要经过的单元的坐标:" << endl;
while (!mes.empty())
{
ss.push_back( mes.top());
cout << mes.top() << endl;
mes.pop();
}
HANDLE hOut;
COORD pos = { 0, 0 };
hOut = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_CURSOR_INFO cci; //定义结构体
GetConsoleCursorInfo(hOut, &cci); //获取光标信息
cci.dwSize = 1; //设置光标大小
cci.bVisible = 0; //设置光标不可见 FALSE
SetConsoleCursorInfo(hOut, &cci); //设置(应用)光标信息
SetConsoleTextAttribute(hOut, 0x0004 | 0x0008 | 0x8000); //设置字体属性
for (int y = 0; y < ss.size();y++)
{
int yy = ss[y] / m; int xx= ss[y] % m;
pos.X = 1+3*xx;
pos.Y = 3+yy;
SetConsoleCursorPosition(hOut, pos); //设置光标坐标
printf("O", 0);
}
}
主函数
#include "DisjSets.h"
#include <iostream>
#include <time.h>
#include"stdlib.h"
#include<conio.h>
#include<graphics.h>
using namespace std;
int main(int argc, const char * argv[]) {
int n;//正方形迷宫的阶数
cout<<"请输入迷宫的阶数"<<endl;
cin >> n;
//输出迷宫的上方边框
cout << " "; //入口
for (int i = 0; i<n - 1; ++i)
cout << "___"; //一个单元占三个位置
cout << "_" << endl;
//将所有可以拆除的墙(下方的墙和右方的墙)编号放进wall中,从起点下方的墙编号1开始
vector<int> wall;
for (int i = 1; i <= n*n * 2 - 2; ++i)
{
if (i % (2 * n) == 0) //排除右边框
continue;
if (i > n*(n - 1) * 2) //排除下边框
continue;
wall.push_back(i);
}
DisjSets s(n*n); //初始化n的平方个单元
//构建迷宫,从wall中随机选择一面墙。若墙两边的单元没有连通,则拆掉墙。
while (wall.size()>0)
{
int size = wall.size();
srand((unsigned)time(NULL));
int j = rand() % size; //随机选择一堵墙
int x = (wall[j] - 1) / 2; //由墙的编号得到单元的下标
if (wall[j] % 2 == 0) //如果编号为偶数,则为右侧的墙
{
if (s.find(x) != s.find(x + 1))
{
s.unionSets(s.find(x), s.find(x + 1));
s.setRightFalse(x);
}
}
else //如果为奇数,则为下方的墙
{
if (s.find(x) != s.find(x + n))
{
s.unionSets(s.find(x), s.find(x + n));
s.setBottomFalse(x);
}
}
wall.erase(wall.begin() + j); //将已经连通的两个单元之间的墙从wall中移除
}
//输出迷宫
for (int i = 0; i<n*n - 1; ++i)
{
if (i%n == 0)
cout << "|"; //左边框
if (s.getBottom(i))
cout << "__";
else
cout << " ";
if (s.getRight(i))
cout << "|";
else
cout << "_";
if ((i + 1) % n == 0)
cout << endl;
}
cout << " |"; //终点
cout << endl;
s.mes.push(0);
s.get(0,n);
s.xiansi(n);
getch();
return 0;
}