前言

接上文的介绍,本文将主要介绍如何生成随机迷宫,在网上找到的资源也比较多,这里我选取了随机 Prim 算法生成迷宫,选择这个算法的理由如下:

算法思想简单,易于实现

生成的迷宫比较自然,不会出现明显的主路

接下来本文将对该算法进行介绍,如果对上一篇推文《数据结构课程设计——迷宫求解(一)》中的可视化不够理解,也没有关系,可视化只是为了结果演示,仅是一个可选项。

01Prim 算法

在开始讲解随机 Prim 算法之前,先来一个题外话,介绍下图论中关于最小生成树的 Prim 算法,便于接下来理解迷宫生成的过程。

下图为一个无向带权图,假定我们先选取了 A 点作为出发点。

从 A 点出发可以有两条路,分别走向 B 和 C ,这时候选择走哪一条就比较重要了,我们希望最后生成树的边上权值之和最小,所以这里我们选择了 B,AB 之间的权值是 3,比 AC 之间的权值 4 更小。

选择了 B 点之后,我们现在有了 A、B 两个点,可以走的边还有 AD、BC、BE,从中继续选出权值最小的边,这次选择了 AD 边。接下来继续考虑 A、B、D 三个点向外可以走的边,每次新加一个点就会新增一些边,考虑的时候总是只选择权值最小的边,而且选择的这条边必须要包含一个未曾访问过的顶点,直到所有的顶点都已经被考虑过了才结束。

最终的生成过程如下图所示。

02随机 Prim 算法生成迷宫

最小生成树的 Prim 算法介绍结束了,接下来就是生成迷宫的重点了。

先说说二者在实现上的区别,在讲到 Prim 算法构造最小生成树的时候,每一次选哪条路径都是有明确目的的,即每次都选权值最小的那条边来向外扩展,现在我们构造迷宫,要保证迷宫是随机生成的,那么就不能够有明确的选择,只能是随机选择。

而和 Prim 算法构造最小生成树类似的是,都是从一个点开始,不停的向外扩展,直到没有可以扩展的点为止。

接下来就看看该如何进行生成,首先肯定得有一个已知的图,我们在这个图上进行生成迷宫,我们使用二维数组表示一个平面的迷宫,其中数字 1 代表墙壁,数字 0 代表地面,假设这个二维数组行列均为 15。实际上根据自己的需要进行设定即可,或者也可以设置不同的难度等级,这里留给读者自行思考,不过需要设置成奇数,否则会出现某一侧的墙是两层这种情况。

初始化代码如下,接下来只要在里面加生成随机迷宫的方法即可

public class RandomMaze {int row;  // 行int col;  // 列int maze[][];  // 二维数组,用于表示迷宫    String color[][];  // 用于记录各点颜色,在随机 Prim 算法中会用到,当然也可以使用其他类型来做一个标记// 初始化,这里直接固定行列值为 21public RandomMaze(){this.row = 15;this.col = 15;this.maze = new int[row][col];this.color = new String[row][col];    }}

随机 Prim 算法生成迷宫之前,需要先对迷宫进行初始化为全 1,也就是全部都是墙壁,然后在其中的奇数行且奇数列进行“挖洞”,也就是将该位置的 1 改成 0,这样就形成了一个 0 被 1 包围的局面,并将墙壁标记为灰色“gray”,地面标记为黄色“yellow”。代码和示意图如下。

    // 初始化迷宫矩阵    public void initMaze(){        // 先全部赋值为 1        for(int i=0; i            for (int j = 0; j                 this.maze[i][j] = 1;                this.color[i][j] = "gray";  // 墙壁标记为灰色            }        // 对奇数行且奇数列进行 “挖洞”        for(int i=0; i2; i++)for (int j = 0; j 2; j++) {this.maze[2*i+1][2*j+1] = 0;this.color[2*i+1][2*j+1] = "yellow";  // “挖洞” 挖出来的 0 标记为黄色            }    }

有了这个挖好洞的迷宫,接下来选一个黄色的 0 ,并将它改成红色的 0,同时,将它的四周墙壁从灰色的 1 变成蓝色的 1,使用随机数,任意抽取一个蓝色的 1,与这个红色 0 只隔着这个选中的蓝色 1 的格子如果是黄色的 0,那么就把这堵墙“打通”,即将选中的这个蓝色 1 改成 0 。同时把新加入的 黄色 0 改成红色,把它周围的墙壁变成蓝色。这一步与最小生成树中逐步向外扩展很类似,只是这里使用了颜色区分。

这里我们直接使用第 1 行第 1 列的 0 作为起点 (整个矩阵由于是数组,所以下标从 0 开始)。

假设随机数选中了右侧的这个蓝色 1,接下来如下图所示

打通墙壁就是将蓝色的 1 变为 0,如果选择的一个蓝色的 1 对面没有黄色的 0,例如我们选择了最上方的蓝色 1,显然对面没有黄色 0,这时候将蓝色 改回 灰色即可。

不断重复上述过程,直到整个迷宫中没有任何一个蓝色的 1 时结束,生成完成。

关于为什么要将黄色的 0 改成红色,这里我只公布答案,如果不改,最终生成的迷宫地图如下。对此感兴趣的读者可以思考下为什么会出现这种情况。

思路介绍完了就直接贴出代码了。

    // 创建随机迷宫    public void createMaze(){        Random rand = new Random();        ArrayList list = new ArrayList<>();        int start_x = 1, start_y = 1;  // 起始点为第 1 行第 1 列        boolean moved = true;  // 是否成功添加一个黄色的 0        int length;        int index;        this.color[start_x][start_y] = "red";  // 先将当前黄色 0 改成红色        while(true){  // 死循环,等待内部break            if(moved) {  // 如果有移动,则将移动后新加结点的周围墙壁也添加进入列表                if (start_x + 1 this.row && this.maze[start_x + 1][start_y] == 1) {                    //this.color[start_x+1][start_y] = "blue";  // 将墙的颜色改为蓝色                    list.add(new Node(start_x + 1, start_y, "Down"));                }                if (start_x - 1 >= 0 && this.maze[start_x - 1][start_y] == 1) {                    list.add(new Node(start_x - 1, start_y, "Up"));                }                if (start_y + 1 this.col && this.maze[start_x][start_y + 1] == 1) {                    list.add(new Node(start_x, start_y + 1, "Right"));                }                if (start_y - 1 >= 0 && this.maze[start_x][start_y - 1] == 1) {                    list.add(new Node(start_x, start_y - 1, "Left"));                }            }            length = list.size();            if(length==0)  break;  // list 为空时结束循环            index = rand.nextInt(length);  // 随机获得一个结点            Node node = (Node)list.remove(index);  // 移除该结点            start_x = node.x;  // 以该结点为新的起始点            start_y = node.y;            moved = false;  // 尚未移动            //  四个判断条件分别为移动方向、边界、是否访问过、是否有路可走            if(node.direction=="Up" && start_x-1>=0 && this.maze[start_x-1][start_y]==0 && this.color[start_x-1][start_y]=="yellow"){  // 试图上移                this.maze[start_x][start_y] = 0;  // 打通墙壁                start_x = start_x - 1;                this.color[start_x][start_y] = "red";                moved = true;            }            if(node.direction=="Down" && start_x+1<this.row && this.maze[start_x+1][start_y]==0 && this.color[start_x+1][start_y]=="yellow"){  // 试图下移                this.maze[start_x][start_y] = 0;                start_x = start_x + 1;                this.color[start_x][start_y] = "red";                moved = true;            }            if(node.direction=="Left" && start_y-1>=0 && this.maze[start_x][start_y-1]==0 && this.color[start_x][start_y-1]=="yellow"){  // 试图左移                this.maze[start_x][start_y] = 0;                start_y = start_y - 1;                this.color[start_x][start_y] = "red";                moved = true;            }            if(node.direction=="Right" && start_y+1<this.col && this.maze[start_x][start_y+1]==0 && this.color[start_x][start_y+1]=="yellow"){  // 试图右移                this.maze[start_x][start_y] = 0;                start_y = start_y + 1;                this.color[start_x][start_y] = "red";                moved = true;            }        }    }

代码当中用到了一句 new Node,这是在创建 Node 类的对象,Node 类代码稍后展示,它就相当于是一个三元组,存储着 。这个移动方向很关键,当我们随机选中一个墙壁时候,能够知道它的“对面”到底在哪里,或者说是往哪个方向走才是对面就是靠的这个移动方向。

代码中能够看到,关于把墙壁改成蓝色这一步,我注释掉了,并且说明了可以不用写,原因也很简单,这里将墙壁这个三元组放入 list 中,就可以代替改成蓝色了,而且之后有从 list 中移除随机选中的三元组这一步,这可以替代如果“打通失败”时候改回灰色这一步,即 list 中存储的就已经代表了是 蓝色的 1 了,移除就代表该选中的 1 已经进行过处理了,即,将该选中的 1 改成 0 或者恢复为 灰色 1。

每次从 list 中随机抽取一个墙壁,需要看沿着它的移动方向,它的“对面”是否越界,以及是否为 黄色的 0,然后才能决定是否要“打通这堵墙”。如果打通了,要将新加入的点的四周墙壁尝试添加到 list 中。

下面附上 Node 类的代码,如果学过 Java 应该能够看出来,这是写在另一个 Java 文件中的。

public class Node {    int x;  // x 坐标    int y;  // y 坐标    String direction;  // 接下来的移动方向    public Node(int x, int y, String direction){        this.x = x;        this.y = y;        this.direction = direction;    }}

现在已经完成了随机生成迷宫的算法,可以在该工程中创建一个文件测试这些方法了,如果能够使用图形界面展示就更直观一点,这里我直接使用了图形界面,上一篇推文介绍过,并不难实现,此处不多解释。

直接另设一个方法,遍历矩阵,数字 1 就画成黑色矩形代表墙壁,数字 0 就画成白色矩形代表地面。某次生成的迷宫如下图所示。

如果对图形界面不感兴趣或者不熟悉,也可以直接将矩阵以方阵形式打印显示出来。

END

在最后Last but not least

本文接上一篇推文内容,利用随机 Prim 算法生成迷宫,简单介绍了随机 Prim 算法的思想,如果学过图论,可以对照着图论中求最小生成树的 Prim 算法进行学习,算法中涉及到修改颜色的部分可能会比较难理解,可以在纸上画图辅助理解。

本文已经实现了生成随机迷宫,下一篇推文将开始介绍如何在一个随机生成的迷宫中从入口出发找到一条通往出口的路。

往期精彩回顾

Python数据结构——树(四)

Python数据结构——树(五)

数据结构课程设计——迷宫求解(一)

数据结构迷宫代码_数据结构课程设计——迷宫求解(二)相关推荐

  1. c语言程序设计迷宫,C语言程序设计课程设计-迷宫.doc

    C语言程序设计课程设计-迷宫 大 学 C语言程序设计 课程设计(论文) 题目: 迷宫问题 院(系): 专业班级: 学 号: 学生姓名: 指导教师: 教师职称: 讲 师 起止时间: 2009.12.14 ...

  2. c语言课程设计走迷宫游戏,C语言课程设计-迷宫游戏.doc

    计算机技术基础课程设计 C语言 设计报告 题目:完整的二维迷宫游戏 学院:工商管理学院 专业:信息系统与信息管理 班级:050507 姓名:孙月 指导教师:张首伟 设计日期:2004年12月10日 题 ...

  3. 《数据结构与算法分析》课程设计——迷宫问题

    中国矿业大学信控学院   补一下我之前在博客园发布的内容  懒得调了,想复制完整代码直接复制最下面的,想复制分布代码去看我博客园链接吧 <数据结构与算法分析>课程设计--迷宫问题 - 刷子 ...

  4. c语言课程设计报告之迷宫,C语言课程设计-迷宫游戏

    <C语言课程设计-迷宫游戏>由会员分享,可在线阅读,更多相关<C语言课程设计-迷宫游戏(15页珍藏版)>请在人人文库网上搜索. 1.计算机技术基础课程设计C语言设计报告题目:完 ...

  5. HTML5期末大作业:动漫网站设计——迪斯尼公主(6个页面) HTML+CSS+JavaScript 动漫网页HTML代码 学生网页课程设计期末作业下载 动漫大学生网页设计制作成

    HTML5期末大作业:动漫网站设计--迪斯尼公主(6个页面) HTML+CSS+JavaScript 动漫网页HTML代码 学生网页课程设计期末作业下载 动漫大学生网页设计制作成 临近期末, 你还在为 ...

  6. HTML5期末大作业:运动系列——NBA篮球主题学生网页设计(7个页面) HTML+CSS+JavaScript 体育网页设计HTML代码 学生网页课程设计期末作业下载 大学生网页设计制作成

    HTML5期末大作业:运动系列--NBA篮球主题学生网页设计(7个页面) HTML+CSS+JavaScript 体育网页设计HTML代码 学生网页课程设计期末作业下载 大学生网页设计制作成 临近期末 ...

  7. HTML5期末大作业:中国传统节日网页设计——端午节(9个页面) HTML+CSS+JavaScript 节日网页HTML代码 学生网页课程设计期末作业下载

    HTML5期末大作业:中国传统节日网站设计--端午节(9个页面) HTML+CSS+JavaScript 节日网页HTML代码 学生网页课程设计期末作业下载 临近期末, 你还在为HTML网页设计结课作 ...

  8. HTML5+CSS期末大作业:运动体育网站设计主题——体育铅球(5页)带注册 期末作业HTML代码 学生网页课程设计期末作业下载 web网页设计制作成品...

    常见网页设计作业题材有 ​​个人. 美食. 公司. 学校. 旅游. 电商. 宠物. 电器. 茶叶. 家居. 酒店. 舞蹈. 动漫. 明星. 服装. 体育. 化妆品. 物流. 环保. 书籍. 婚纱. 军 ...

  9. HTML5期末大作业:中国传统节日网页设计——端午节(9个页面) HTML+CSS+JavaScript 节日网页HTML代码 学生网页课程设计期末作业下载...

    HTML5期末大作业:中国传统节日网站设计--端午节(9个页面) HTML+CSS+JavaScript 节日网页HTML代码 学生网页课程设计期末作业下载 临近期末, 你还在为HTML网页设计结课作 ...

最新文章

  1. html年月日下拉联动菜单 年月日三下拉框联动
  2. webkit如何实现JS DOM binding—基于V8分析
  3. 不同国家的视力表也不一样!| 今日趣图
  4. 02-05 Python库-time datetime
  5. Linux内核与Linux操作系统的区别,[科普] Linux 的内核与 Linux 系统之间的关系
  6. 数据结构和算法系列13 五大查找之哈希查找
  7. cocos2dx-lua 批量打包及修改
  8. C#笔记01 课程总论
  9. nandflash驱动详解
  10. python期权定价代码_pythonspan data-e=6Zqc56KN/span式期权定价公式_Python_脚本语言_IT 经验_爱安网 LoveAn.com...
  11. 读《我喜欢生命本来的样子》记(二)
  12. 《支付机构外汇业务管理办法》正式发布 合作银行不得超过2家
  13. fast-lio 卡尔曼滤波
  14. DNS(域名解析系统)协议
  15. C语言实现PID之应用
  16. 可以这样理解视觉Transformer模型中patch交互的关系
  17. js中[object,object]是什么,怎么取值
  18. 深究跨dll的资源分配和释放问题
  19. 想知道如何文字转语音真人发声?这3款工具轻松实现
  20. .map文件 源映射(Source Map)详解

热门文章

  1. 夜来香——暗恋的滋味
  2. iQOO Neo6现身安兔兔数据库:高导热稀土散热加入 跑分轻松破百万
  3. 华为P50E全方位曝光:延续P50设计 换装骁龙778G 4G芯片
  4. 厉害了!同事请假参加冬奥会顺手得了铜牌:系知名涂料公司员工
  5. 传新一轮估值200亿美金 小红书回应:以老股东增持为主
  6. 1-9月欧洲新能源车份额上升 混动车注册量增加8.8%
  7. 联发科发布天玑5G开放架构 采用该定制芯片终端7月上市
  8. 京东618期间将累计发放百亿消费券
  9. 外媒确认iPhone 13 Pro系列采用120Hz OLED面板:支持智能调节屏幕刷新率
  10. 让猫给人打工,猫咖是一门好生意吗?