应用不相交集类生成迷宫

本文介绍了一个使用不相交集类构建n阶正方形迷宫的算法。通过随机选择可拆的墙并检查墙两侧单元是否连通来构建迷宫。最终形成的迷宫中任意两点间路径唯一。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

应用不相交集类(采用高度求并和路径压缩查找)构建一个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;
}

结果
这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值