迷宫
作者:互联网
迷宫游戏
深度优先遍历(DFS)
顾名思义DFS就是从一个节点出发直到不能访问然后回转到上一层 也就是所说的“回溯+递归”
该方法我学习了老师PPT上的内容,可以这样理解:
(1)访问顶点v
(2)从v的未被访问的邻接点中选取一个顶点w,重复第一步,如果没有未被访问的邻接点,回溯至上一顶点
(3)重复前两步,直至图中所有和v路径相通的顶点都被访问到
总的来说就是,回溯+递归
此算法参照老师PPT教学上的代码,基本可以看懂。
C++部分代码实现:
void Creat_Map01(int x_index, int y_index)
{
int x_tmp, y_tmp;
int tag = 0;//做标记,看当前位置的四周是否还有有效通道Road
int rand_Direction;//随机生成四个方向
rand_Direction = rand() % 4;//随机生成四个方向
if (rand_Direction == 0 && x_index >= 3 && map[x_index - 2][y_index] == Road)//向上
{ x_index = x_index - 2;}
else if (rand_Direction == 1 && x_index < count - 3 && map[x_index + 2][y_index] == Road)//向上
{ x_index = x_index + 2;}
else if (rand_Direction == 2 && y_index > 3 && map[x_index][y_index - 2] == Road)//向左
{ y_index = y_index - 2;}
else if (rand_Direction == 3 && y_index < count - 3 && map[x_index][y_index + 2] == Road)//向右
{ y_index = y_index + 2;}
while (1)
{ tag = IsHaveNeighbor(x_index, y_index);
if (tag == 0)
return;
else
{
map[x_index][y_index] = Red;
x_tmp = x_index;
y_tmp = y_index;
//产生四个随机方向的新坐标;
map[(x_tmp + x_index) / 2][(y_tmp + y_index) / 2] = Red;
map[x_index][y_index] = Red;
Creat_Map01(x_index, y_index);
}
}
}
A*算法
在计算机科学中,A*算法作为Dijkstra(迪杰斯特拉)算法的扩展,是一种静态路网中求解最短路径有效的直接搜索方法,因其高效性被广泛应用于寻路及图的遍历中
首先明确几个概念:
搜索区域
搜索区域可以划分为正方形格子,但不限于此,六边形,矩形,平行四边形都可以。因此他们的中心点通常称为节点,而不是方格。
路径排序
下一步怎么移动由以下公式确定:F(n)=G+H。F(n)为估价函数,G代表的是从初始位置Start沿着已生成的路径到指定待检测结点移动开销。H表示待检测结点到目标节点B的估计移动开销。
启发函数
H为启发函数,可以看作是一种试探,由于在找到唯一路径前,不确定在前面会出现什么障碍物,因此用了一种计算H的算法,具体可以根据实际情况决定。为了简化问题,H采用的是传统的曼哈顿距离,也就是横纵向走的距离之和。
开放列表
将寻路过程中待检测的结点存放于Open List中,而已检测过的结点则存放于Close List中。
以上是A* 的关键点,尤其是启发函数,如果没有启发函数,则A* 就退化成了Dijkstra算法,是运行效率重要还是找到最佳路径重要,全靠启发函数来调节,因此也被归为启发式算法。
A*算法的具体步骤
- 把起点加入openlist
- 遍历openlist,找到F值最小的节点,把它作为当前处理的节点,并把该节点加入closelist中
- 对该节点的8个相邻格子进行判断, 如果格子是不可抵达的或者在closelist中,则忽略它,否则如下操作:
a. 如果相邻格子不在openlist中,把它加入,并将parent设置为该节点和计算f,g,h值
b.如果相邻格子已在openlist中,并且新的G值比旧的G值小,则把相邻格子的parent设置为该节点,并且重新计算f值。 - 重复2,3步,直到终点加入了openlist中,表示找到路径;或者openlist空了,表示没有路径。
参考文章:
A算法详解
A算法入门
https://blog.csdn.net/yghlqgt/article/details/109608550
A* 算法部分,参照了网络上的大量资料,虽然原理基本可以理解,但真正实现起来还是有些困难,故参考了CSDN上的各位大神的部分代码。
public static Grid aStarSearch(Grid start, Grid end) {
//准备两个链表,分别存储 将要选择的节点 和 已经走过的节点
ArrayList<Grid> openlist=new ArrayList<Grid>();
ArrayList<Grid> closelist=new ArrayList<Grid>();
//将起点加入链表,准备开始寻路。
openlist.add(start);
//只要链表不为空,就重复这个寻路的过程
while(openlist.size()>0) {
//找到 openlist中 F值最小的那个 方格(节点)
Grid currentgrid=findMinGrid(openlist);
//从 openlist中删除找到的那个 F值 最小的那个节点
openlist.remove(currentgrid);
//将这个 F值 最小的节点,加入到 closelist 中
closelist.add(currentgrid);
//寻找 当前找到的这个 F值最小的节点的 邻居节点 ——上下左右,四个方向上的节点,要判断它们是否为合法可用的节点。
List<Grid> neighbors=findNeighbors(currentgrid, openlist, closelist);
//对合法可用的邻居节点进行初始化,并加入到 openlist中
for(Grid grid : neighbors) {
if(!openlist.contains(grid)) {
grid.initGrid(currentgrid,end);
openlist.add(grid);
}
}
//邻居节点加入 openlist 后,判断openlist中,是否包含 终点节点,如果包含终点,直接返回并退出。
for(Grid grid : openlist) {
if((grid.x==end.x) && (grid.y==end.y)) {
return grid;
}
}
}
return null;
}
//寻找邻居节点的方法,返回值为 链表 ——创建一个合理的邻居链表
private static ArrayList<Grid> findNeighbors(Grid grid, List<Grid> openlist, List<Grid> closelist) {
ArrayList<Grid> gridlist=new ArrayList<Grid>();
//判断上下左右邻居节点的合理性,没问题就加入到邻居链表中。
if(isValidGrid(grid.x, grid.y-1, openlist, closelist)) {//下
gridlist.add(new Grid(grid.x, grid.y-1));
}
if(isValidGrid(grid.x, grid.y+1, openlist, closelist)) {//上
gridlist.add(new Grid(grid.x, grid.y+1));
}
if(isValidGrid(grid.x-1, grid.y, openlist, closelist)) {//左
gridlist.add(new Grid(grid.x-1, grid.y));
}
if(isValidGrid(grid.x+1, grid.y, openlist, closelist)) {//右
gridlist.add(new Grid(grid.x+1, grid.y));
}
return gridlist;
}
//判断当前位置的节点是否合理
private static boolean isValidGrid(int x, int y, List<Grid> openlist, List<Grid> closelist) {
//当前节点是否越界,不再MAZE数组范围内了,注意二位数组的长度计算方法及含意
//MAZE。length表示行的长度
//MAZE[0]。length表示列的长度
if(x<0 || x>=MAZE.length || y<0 || y>=MAZE[0].length) {
return false;
}
//当前节点是否为障碍节点
if(MAZE[x][y]==1) {
return false;
}
//判断当前节点是否在 openlist中
if(containgrid(openlist, x, y)) {
return false;
}
//判断当前节点是否在 closelist中
if(containgrid(closelist, x, y)) {
return false;
}
return true;
}
//判断当前链表中是否包含当前的节点
private static boolean containgrid(List<Grid> grids, int x, int y) {
for(Grid grid : grids) {
if((grid.x==x) && (grid.y==y)) {
return true;
}
}
return false;
}
//寻找当前链表中的节点F值 最小的那个节点,并返回这个节点。
private static Grid findMinGrid(ArrayList<Grid> openlist) {
Grid tempgrid=openlist.get(0);
for(Grid grid : openlist) {
if(grid.f<tempgrid.f) {
tempgrid=grid;
}
}
return tempgrid;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
int b1=0,b2=0;//起始坐标
int c1=0,c2=6;
Grid startgrid=new Grid(b2,b1);
System.out.println("起点坐标为:"+"("+b1+","+b2+")");
Grid endgrid=new Grid(c2,c1);
System.out.println("终点坐标为:"+"("+c1+","+c2+")");
Grid resultgrid=aStarSearch(startgrid,endgrid);
//创建回溯链表
ArrayList<Grid> path=new ArrayList<Grid>();
while(resultgrid!=null) {
path.add(new Grid(resultgrid.x, resultgrid.y));
resultgrid=resultgrid.parent;
}
//打印输出当前寻路路径
int count=0;
for(int i=0; i<MAZE.length; i++) {
for(int j=0; j<MAZE[0].length; j++) {
if(containgrid(path, i, j)) {
System.out.print("走, ");
count++;
}
else
{
System.out.print(MAZE[i][j]+ ", ");
}
}
System.out.println();
}
System.out.println("最短路径长度为:"+count);
}
参考链接:
https://blog.csdn.net/xgf415/article/details/75200047
https://blog.csdn.net/Dog_dream/article/details/80270398
https://mochen.blog.csdn.net/article/details/109558689
https://blog.csdn.net/little_mind/article/details/45920961
https://blog.csdn.net/weixin_46037153/article/details/107136560
https://blog.csdn.net/u010944926/article/details/20635829
https://blog.csdn.net/ws_PersonalSpace/article/details/83420458
标签:index,openlist,迷宫,closelist,grid,Grid,节点 来源: https://blog.csdn.net/Smileyiii/article/details/111598648