C#实现4种经典迷宫生成算法和迷宫寻路算法(二)

使用并查集算法生成迷宫

我们把迷宫先初始化为这样一个矩阵:每一个格子互不相连,如果使用区域的定义的话,每个格子就是一个区域。如果迷宫矩阵大小是m*n,那它在最开始拥有m*n个区域。

(1)随机选择两个相邻的格子,把它们合成一个区域。

如下图所示,当我们把5和6合并时,2、4、5、6、9将变成同一个区域。

(2)在每一次合并后,我们都检查起点S和终点T是否在同一个区域里。如果是,程序结束;如果不是,重复第一步。

使用并查集实现迷宫的原理就是这么简单!

 

并查集的原理本文并不打算详细讲解,读者可搜索相关文章阅读。

网上的并查集实现一般都是使用一维数组的,本程序为了方便,改成了二维数组,其实质是差不多的。

并查集核心代码如下:

private Position Find(Position x)
{
    Position r = x;
    while (rooms[r.row, r.col] != r)
        r = rooms[r.row, r.col];//找到他的前导结点
    Position i = x, j;
    while (i != r)//路径压缩算法
    {
        j = rooms[i.row, i.col];//记录x的前导结点
        rooms[i.row, i.col] = r;//将i的前导结点设置为r根节点
        i = j;
    }
    return r;
}

private void Join(Position x, Position y)
{
    Position a = Find(x);//x的根节点为a
    Position b = Find(y);//y的根节点为b
    if (a != b)//如果a,b不是相同的根节点,则说明ab不是连通的
    {
        rooms[a.row, a.col] = b;//我们将ab相连,将a的前导结点设置为b
    }
}

然后就是怎么使用并查集生成迷宫了,代码如下:

private Position[,] rooms;//空白
private List<Position> remain_wall1;//竖墙
private List<Position> remain_wall2;//横墙

public override void Build()
{
    rooms = new Position[room_row, room_col];
    remain_wall1 = new List<Position>();
    remain_wall2 = new List<Position>();

    for (int i = 0; i < ROW; i++)
    {
        for (int j = 0; j < COL; j++)
        {
            if (i == 0 || i == ROW - 1 || j == 0 || j == COL - 1)
            {
                maze[i, j] = 1;
            }
            else
            {
                if (i % 2 == 0)
                {
                    maze[i, j] = 1;

                    if (j % 2 == 1)
                    {
                        remain_wall2.Add(GetPosition(i / 2 - 1, (j - 1) / 2));
                    }
                }
                else
                {
                    if (j % 2 == 0)
                    {
                        maze[i, j] = 1;

                        remain_wall1.Add(GetPosition((i - 1) / 2, j / 2 - 1));
                    }
                    else
                    {
                        maze[i, j] = 0;

                        int r = (i - 1) / 2;
                        int c = (j - 1) / 2;
                        rooms[r, c] = GetPosition(r, c);
                    }
                }
            }
        }
    }

    Random rand = new Random();
    while (Find(GetPosition(0, 0)) != Find(GetPosition(room_row - 1, room_col - 1)))
    {
        int index = rand.Next(remain_wall1.Count + remain_wall2.Count);
        Position wall = null;
        int wall_type = 0;
        if (index >= remain_wall1.Count)
        {
            wall = remain_wall2[index - remain_wall1.Count];
            wall_type = 2;
        }
        else
        {
            wall = remain_wall1[index];
            wall_type = 1;
        }

        Position room1 = null;
        Position room2 = null;
        if (wall_type == 1)
        {
            room1 = GetPosition(wall.row, wall.col);
            room2 = GetPosition(wall.row, wall.col + 1);
        }
        else
        {
            room1 = GetPosition(wall.row, wall.col);
            room2 = GetPosition(wall.row + 1, wall.col);
        }

        if (Find(room1) == Find(room2))
        {
            continue;
        }

        if (wall_type == 1)
        {
            remain_wall1.RemoveAt(index);
            maze[wall.row * 2 + 1, (wall.col + 1) * 2] = 0;
        }
        else
        {
            remain_wall2.RemoveAt(index - remain_wall1.Count);
            maze[(wall.row + 1) * 2, wall.col * 2 + 1] = 0;
        }

        Join(room1, room2);
    }
}

程序是很好理解的。就是把所有墙都放到一个列表里,每次循环拆掉一堵墙,从列表去掉这堵墙,然后检查起点S和终点T是不是在同一个区域。

使用并查集生成的迷宫如下图所示:

其特点是:不是所有点都在同一个区域,也就是有些格子是怎么走都走不到的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值