应用不相交集类(采用高度求并和路径压缩查找)构建一个n阶正方形迷宫,迷宫拥有n的平方个单元,每个单元拥有三个数据成员。一个数据成员存储父节点,如果是根节点则存储树的高度的负值减一。另两个数据成员分别代表单元的下侧墙和单元的右侧墙。再额外添加上边框和左边框就构成迷宫。
一开始所有的单元相互不连通,构建时随机选择一堵可以拆的墙,如果墙两侧的单元不连通,联通他们并拆掉墙。如果联通就不进行操作。进行到最后,所有的单元都被联通。
由于所有单元最后形成了一棵树,因此总能从一个单元到另一个单元并且路径唯一。
编写代码时犯的错误:没有将cell构造函数声明为explicit并且 s[root1].element=root2写成了s[root1]=root2。编译器隐式的将root2转换成了cell类型,导致多了很多面墙。
头文件
#include <vector>
class DisjSets
{
public:
explicit DisjSets(int numElements);
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;
}
private:
struct cell
{
int element;
bool bottom; //下方的墙,true则代表有墙
bool right; //右方的墙,true则代表有墙
explicit cell(int ele=-1,bool bot=true,bool rg=true):
element(ele),bottom(bot),right(rg){}
};
std::vector<cell> s;
};
cpp文件
#include "disjointSet.hpp"
DisjSets::DisjSets(int numElements):s(numElements)
{
for(auto x:s)
{
x.element=-1;
x.bottom=true;
x.right=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;
}
main.cpp文件
#include "disjointSet.hpp"
#include <iostream>
#include <time.h>
#include"stdlib.h"
using namespace std;
const int n=30; //正方形迷宫的阶数
int main(int argc, const char * argv[]) {
//输出迷宫的上方边框
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) //排除下边框
++i;
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;
return 0;
}
结果