c语言 迷宫深度遍历 算法,图的遍历迷宫生成算法浅析
1.
引言 在平常的游戏中,我们常常会碰到随机生成的地图。这里我们就来看看一个简单的随机迷宫是如何生成。
2. 迷宫描述随机生成一个m *
n的迷宫,可用一个矩阵maze[m][n]来表示,如图:
图1.1 图1.2
这里是两个迷宫的例子,其中“■”表示障碍物(Obstacle
block)。以图1.1迷宫为例,我们可用一个9 * 9的矩阵来表示:
1 1 1 1 1 1 1 1 1
0 0 0 0 0 0 0 0 1
1 1 1 1 1 1 1 0 1
1 0 0 0 0 0 1 0 1
1 0 1 1 1 1 1 0 1
1 0 1 0 0 0 0 0 1
1 0 1 0 1 1 1 1 1
1 0 0 0 0 0 0 0 0
1 1 1 1 1 1 1 1 1
(矩阵中1表示是障碍物,0表示可以行走)
3、迷宫生成算法
(蓝色表示可以行走,棕色表示是墙壁)
图3.1
如图3.1所示为迷宫的初始化情形,迷宫如果除去其迷宫的外围框架就是一个7*7的矩阵,如果要生成一个完整的迷宫,那么就要遍历完图3.1所示的每一个可以行走的点,其中遍历的点也包含了入口和出口,如果每一个点都遍历完了就会生成一棵完整的遍历树,这棵遍历树包含了入口和出口所以这颗树所描述的迷宫是有解的(即:入口到出口时连通的。)图3.2就表示图1.1迷宫遍历所得到的遍历树,树包含了入口和出口所以此迷宫有解。
图3.2
图3.3
图3.3表示的是图3.2进一步的处理后得到的最终迷宫图,就是在图3.2的基础上把遍历树上的是墙壁的元素置为可行走的元素。
3.1元素的遍历
图3.1.1描述了各个遍历点的连接关系其中1元素为起始遍历点,49元素为终点遍历点我们声明一个mazepoint类用来描述每一个遍历点xtemp表示当前点在数组中的x坐标,ytemp表示当前点在数组中的y坐标,定义为mazepoint对象的next用来链表一个点的地址last用来链表上一个地址。
图3.1.1
声明了两个mazepoint的变量head和tail用于存储迷宫的链表的头地址和尾地址由于在迷宫刚刚开始的时候初始化元素是第一个所以头尾是相同的在主函数中把head赋值给了p1,(p1是我们声明的一个临时存储的变量;p1和p2用于新的链表元素生成)
如图3.1.2所示元素1它连接到元素3和元素15,所以它可以遍历这两个元素,假设遍历的方向是随机的,如果第一次现在向右遍历那么元素3就被遍历并把它标志为flag(flag表示已经遍历过了不容许再次遍历),所以元素1和元素3都标志位flag说明在其它元素想遍历它们的时候是不能再次被遍历了。这就有可能会遍历成一棵完整的树。
图3.1.2
如图3.1.3所示图1.1迷宫在生成时前13步,据图我们可知在第13步的时候遍历到元素19,但是元素19周围的点都已经遍历过但同时还有其它元素还没有遍历到,所以要后退到上一个遍历过的元素判断是否有遍历的方向,所以链表到元素17,但是此元素也没有可遍历的方向所以要一直往上链表直到链表到某一个可以重新遍历的点为之。
图3.1.3
图3.1.4是往上链表的示意图直到链表到元素45发现此元素拥有可遍历的方向,所以又重新往下建立新的链表,即元素47
图3.1.4
图3.1.5是最终遍历完后的遍历路径,此时只要沿着遍历的方向把相应的“墙”拆掉就可以生成一个无外框的迷宫并且只能生成奇数行和奇数列的迷宫。
图3.1.5
图3.1.6是程序生成得33*33的迷宫
图3.1.6
4、编程思路
5、源代码
#include
#include
using std::cout;
using std::cin;
using std::endl;
#define Row 33 //用于定义迷宫的行必须为奇数,否则不能遍历到所以的点
#define Col 33 //用于定义迷宫的列必须为奇数,否则不能遍历到所以的点
#define flag
1 //点的标志位,如果找到符合条件的点就把它置为flag,只要某一个点置位为flag就不能再次被访问;
int x_row=0; //迷宫的起始点位置的x坐标
int y_col=0; //迷宫的起始点位置的y坐标
int
Left=0; //初始化向左的方向标志位Left
int Right=0; //初始化向右的方向标志位Right
int Up=0; //初始化向上的方向标志位Up
int
Dwon=0; //初始化向下的方向标志位Dwon
int
pointcount=((Row+1)*(Col+1)/4-1); //初始化要遍历的点数,(因为第一个点不要遍历了,所以总点数要减去1)
int get_count(); //定义可以获得某一个点可以行走的方向数的函数
class mazepoint //定义一个类用于存放每一个遍历点的坐标
{
public:
int xtemp; //用于存放一个遍历点的x坐标
int ytemp; //用于存放一个遍历点的y坐标
mazepoint
*next; //用于存放一个遍历点的下一个遍历点的地址
mazepoint
*last; //用于存放一个遍历点的上一个遍历点的地址
};
mazepoint *p1;
mazepoint *p2;
mazepoint
*head=NULL; //初始化头由于没有指向如何位置所以赋值为NULL
mazepoint
*tail=NULL; //初始化尾由于没有指向如何位置所以赋值为NULL
int mazemap[Row][Col]= //初始化迷宫地图数组
{
1
};
int main()
{
p1=new
mazepoint; //声明一片内存空间
p1->xtemp=0; //初始化类元素x坐标
p1->ytemp=0; //初始化类元素y坐标
p1->last=NULL; //初始类只有一个元素没有前一个元素,链接到别的元素所以它的的上一个遍历点位空NULL
head=p1; //把链表的头地址赋值给p1
tail=p1; //刚刚开始没有其它元素所以把链表的尾地址也是p1
int
mazenum=0; //用于统计遍历的次数
srand(time(0)); //用当前时间做随机种子数
while(pointcount) //如果没有遍历完所有的点就继续遍历,以确保每一次的随机数都不一样
{ int
rdNo=rand(); //获得随机数
int
Randtemp; //定义一个变量,用于存储处理后的随机数
int
tempflag; //定义一个变量,用于存储某一个点可以行走的方向数
tempflag=get_count();//获得某一个点可以行走的方向数
if(tempflag!=0) //如果某一个点可以行走就处理随机数然后随机选择行走的方向
Randtemp=rdNo%tempflag; //根据返回的可以行走的方向提供行走方向数
else
{
Randtemp=NULL; //如果不能行走就赋值为NULL
}
cout<
<<
rdNo<
Randtemp+=1; //加1是为了和定义的方向Left,Right,Up,Dwon相匹配
cout<
<<
Randtemp<
if(tempflag!=0) //如果可以行走
{
p2=p1;
p1->xtemp=x_row; //保存当前点的x坐标
p1->ytemp=y_col; //保存当前点的y坐标
p1=new
mazepoint; //声明内存
p2->next=p1; //前一个遍历点链表到下一个点的地址
p1->last=p2; //后一个遍历点链表到上一个点的地址
p1->next=NULL; //新生产的点没有链表到下一个点所以设为NULL。(新产生的点是链表的最后一个点)
if(Randtemp==Left
&&
mazemap[x_row][y_col-2]!=flag) //判断左边是否可以走如果可以走就不为;flagRandtemp==Left表示随机数等于Left
{
//***********************************************************************
// ↓mazemap[x_row][y_col];新遍历到的位置 *
// ■□▲←上一次确定的位置 *
// ↑mazemap[x_row][y_col+1];新遍历到位置的相同方向的临近元素 *
//***********************************************************************
y_col-=2; //如果可以左走就要把对应的坐标也要改变;也就是行值减2
mazemap[x_row][y_col]=flag; //把遍历到的新的迷宫地图元素也要置flag
mazemap[x_row][y_col+1]=flag;//把遍历到的新的迷宫地图元素相同方向的临近元素也要置flag
--pointcount; //遍历到新的一个点,那么遍历点的总数pointcount减1
cout<
choice
Left"<
}
else
if(Randtemp==Right &&
mazemap[x_row][y_col+2]!=flag)//判断右边是否可以走如果可以走就不为;flagRandtemp==Right表示随机数等于Right
{
//***********************************************************************************
// ↓mazemap[x_row][y_col];新遍历到的位置 *
// 上一次确定的位置→▲□■ *
// ↑mazemap[x_row][y_col-1];新遍历到位置的相同方向的临近元素 *
//***********************************************************************************
y_col+=2; //如果可以右走就要把对应的坐标也要改变;也就是行值加2
mazemap[x_row][y_col]=flag; //把遍历到的新的迷宫地图元素也要置flag
mazemap[x_row][y_col-1]=flag;//把遍历到的新的迷宫地图元素相同方向的临近元素也要置flag
--pointcount; //遍历到新的一个点,那么遍历点的总数pointcount减1
cout<
choice Right"<
}
else
if(Randtemp==Up &&
mazemap[x_row-2][y_col]!=flag)//判断上边是否可以走如果可以走就不为;flagRandtemp==Up表示随机数等于Up
{
//***********************************************************************************
// ■←mazemap[x_row][y_col];新遍历到的位置 *
// □←mazemap[x_row+1][y_col];新遍历到位置的相同方向的临近元素 *
// 上一次确定的位置→▲ *
//***********************************************************************************
x_row-=2; //如果可以上走就要把对应的坐标也要改变;也就是列值减2
mazemap[x_row][y_col]=flag; //把遍历到的新的迷宫地图元素也要置flag
mazemap[x_row+1][y_col]=flag;//把遍历到的新的迷宫地图元素相同方向的临近元素也要置flag
--pointcount; //遍历到新的一个点,那么遍历点的总数pointcount减1
cout<
choice Up"<
}
else
if(Randtemp==Dwon &&
mazemap[x_row+2][y_col]!=flag)//判断下边是否可以走如果可以走就不为;flagRandtemp==Dwon表示随机数等于Dwon
{
//***********************************************************************************
// 上一次确定的位置→▲ *
// □←mazemap[x_row-1][y_col];新遍历到位置的相同方向的临近元素 *
// ■←mazemap[x_row][y_col];新遍历到的位置 *
//***********************************************************************************
x_row+=2; //如果可以下走就要把对应的坐标也要改变;也就是列值加2
mazemap[x_row][y_col]=flag; //把遍历到的新的迷宫地图元素也要置flag
mazemap[x_row-1][y_col]=flag;//把遍历到的新的迷宫地图元素相同方向的临近元素也要置flag
--pointcount; //遍历到新的一个点,那么遍历点的总数pointcount减1
cout<
choice Dwon"<
}
tail=p1; //把链表的尾巴赋值给新产生的点。(新产生的点是链表的最后一个点)
}
else //如果不可以行走就链表到上一个点直到链表到的某一个点可以行走为止
{
cout<
To Last Point..."<
if(tail==head) //如果是第一个遍历点那么链表的头和尾巴重合
{
tail=head;
return
0;
}
else
{
tail=tail->last; //链表到上一个点地址
if(tail->last
==NULL)
tail=head; x_row=tail->xtemp; //读取上一个遍历点的x坐标
y_col=tail->ytemp; //读取上一个遍历点的y坐标 }
++mazenum; //执行次数加1
cout<
is:"<
cout<
Point coordinate is"<
is:"<xtemp<
y
is:"<ytemp<
cout<
Point coordinate is"<
is:"<
y
is:"<
}
if(pointcount==0) //如果每一个遍历点都遍历到了就打印地图,生成得迷宫是没有边界,即:没有外围墙的迷宫
{
for(int
C=0;C
cout<
cout<
for(int
R=0;R
{
if(R==0) //入口不能输出围墙
cout<
//打印入口
else
cout<
for(int
C=0;C
{
if(mazemap[R][C]==flag)//如果迷宫数组的元素是路的话就打印空白
cout<
else //如果迷宫数组的元素是墙壁(flag)的话就打印墙■
cout<
}
if(R==Row-1) //打印出口
cout<
else
cout<
cout<
}
for(
C=0;C
cout<
cout<
}
}
return 0;
}
int get_count()
{
int
count=0; //初始化用于某一个点可以行走的方向数,(count用于统计某一个点可以行走的方向数)
count=0;
if(y_col-2<0) //判断往左是否越界;即:是否超出迷宫数组
{
if(y_col<0)
y_col=0;
Left=0;
cout<
not is Direction "<
is:"<
}
else
{
if(mazemap[x_row][y_col-2]!=flag)//判断往左是否可以走
{
++count;
Left=count;
}
else
{
cout<
not is Direction "<
is:"<
}
}
if(y_col+2>Col) //判断往右是否越界;即:是否超出迷宫数组
{
if(y_col>Col)
y_col=Col;
Right=0;
cout<
not is Direction "<
is:"<
}
else
{
if(mazemap[x_row][y_col+2]!=flag)//判断往右是否可以走
{
++count;
Right=count;
}
else
{
cout<
not is Direction "<
is:"<
}
}
if(x_row-2<0) //判断往上是否越界;即:是否超出迷宫数组
{
if(x_row<0)
x_row=0;
Up=0;
cout<
not is Direction "<
is:"<
}
else
{
if(mazemap[x_row-2][y_col]!=flag)//判断往上是否可以走
{
++count;
Up=count;
}
else
{
cout<
not is Direction "<
is:"<
}
}
if(x_row+2>Row) //判断往下是否越界;即:是否超出迷宫数组
{
if(x_row>Row)
x_row=Row;
Dwon=0;
cout<
not is Direction "<
is:"<
}
else
{
if(mazemap[x_row+2][y_col]!=flag)//判断往下是否可以走
{
++count;
Dwon=count;
}
else
{
cout<
not is Direction "<
is:"<
}
}
return count;
}
c语言 迷宫深度遍历 算法,图的遍历迷宫生成算法浅析相关推荐
- 【算法】组合数学——排列数生成算法详解(一)
组合数学中的全排列深成算法历来是组合数学考试的重要考察点,因此在这里我简单的介绍一下6种全排列生成算法的详细过程,并借此比较它们之间的优劣之处. 不论是哪种全排列生成算法,都遵循着"原排列& ...
- 图论算法——图的遍历
图论算法也是非常基础且重要的算法(ps:好像没有不重要的......) 图的基本应用--图的遍历,从具体的题目着手,学习图的遍历方式及代码形式. 我们先来看一下题目,然后再具体分析图的遍历方式. 题目 ...
- 【论文翻译】Cluster-GCN:用于训练深度和大型图卷积网络的高效算法
Cluster-GCN: An Efficient Algorithm for Training Deep and Large Graph Convolutional Networks 聚类GCN:一 ...
- 数据结构与算法--图的表示与常用算法
什么是图? 图(Graph)形结构中,是一种非线性结构,在图中每一个元素都可以有0或多个前驱,也可以有多个后驱.节点之间的关系是任意的,即图中任意两个数据元素之间都有可能相关. 图的术语 顶点:带有数 ...
- 树的遍历和图的遍历的异同
一.认识的理清 1. 应该认识到"深度优先"和"广度优先"是算法思想,而递归是实现"深度优先"的一种方法(深度优先可以非递归实现): 2. ...
- 基于高度图的三维地形生成算法入门篇 —— 均匀网格地形生成算法
赵 刚 引言 在三维游戏等建立的虚拟世界中要求虚拟场景具有很高的逼真度,其中的三维地形逼真度是关键之一.然而三维地形的生成和绘制需要巨大的计算量,实景地形的生成还需要地形数据库的支持,在运算能 ...
- java抢红包算法_Java抢红包的红包生成算法
马上过年了.过年微信红包很火,最近有个项目也要做抢红包,于是写了个红包的生成算法. 红包生成算法的需求 预先生成所有的红包还是一个请求随机生成一个红包 简单来说,就是把一个大整数m分解(直接以&quo ...
- C语言数据结构与算法---图的遍历
文章目录 一. 深度优先搜索(DFS) 1.概念及实现原理 2. 深度优先遍历的实现代码 1. 若图为邻接矩阵 2. 若图为邻接表 二. 广度优先搜索(BFS) 1. 概念及实现原理 2. 广度优先遍 ...
- 数据结构学习笔记——图的遍历算法(深度优先搜索和广度优先搜索)
目录 一.图的遍历概念 二.深度优先搜索(DFS) (一)DFS算法步骤 1.邻接表DFS算法步骤 2.邻接矩阵DFS算法步骤 (二)深度优先生成树.森林 (三)DFS的空间复杂度和时间复杂度 三.广 ...
最新文章
- ssl双向认证和单向认证的区别
- 点滴积累【C#】---操作文件
- 用代码创建工程并添加内容
- php的cookie教程,PHP4之COOKIE支持详解
- C# 导出dataGridView中的值到Excel
- Linux下编译Jsoncpp
- 关于一致性hash算法的几个问题
- 使用WinDbg分析Windows dump文件方法
- day01(计算机基本知识+JAVA基础知识+环境变量的配置+标识符命名规则+注释的分类)
- 某天的零点时刻0:0:0 和截至时刻23:59:59
- 订单可视化(智能制造、流程再造、企业信息化) 第二篇 背景及问题提出
- 深入理解 Java 中的转义符: \u000a\u0022
- 消费者大喊商家欺诈!宝贝ID下的商品违规修改也是定时炸弹!
- GD25Qxxx使用笔记
- count的几种写法
- 台阶的意思_正屋大门前几步台阶好 二步台阶进屋的含义 三步台阶进屋的含义...
- Bugku杂项——论剑
- matlab 调制 解调,基于Matlab调制与解调的实现
- opencv 图像操作,常用 OpenCV 内置函数
- sorted()函数,实现排序