【算法题集锦之三】--迷宫的随机生成和路径搜索
2014-07-20 16:52
459 查看
这个题目和数据结构---图有关,由于我对图掌握的不是很熟悉,所以写这篇博客来加深自己的理解。
迷宫的随机生成和路径搜索主要和图的遍历有关,一般来说图的遍历主要有两种方式:1、深度优先遍历(DFS)2、广度优先遍历(BFS)
两种遍历方式都很好理解,就说说深度优先遍历:
深度优先遍历,顾名思义,就是尽可能往深处遍历,访问到一个节点时,搜索这个节点没有被访问过的相邻节点,选择一个继续做同样的操作,直到没有邻节点为止再回溯到上一个访问的节点,并选择另外的邻节点。
可以这样描述深度遍历:
(1)访问顶点v;
(2)从v的未被访问的邻接点中选取一个顶点w,重复第一步,如果v没有未访问的邻接点,回溯至上一顶点;
(3)重复上述两步,直至图中所有和v有路径相通的顶点都被访问到。
例如上图,一个可能的遍历顺序就是1-->2-->5-->6-->3-->7-->8-->4-->9-->10。
那么,迷宫的生成和路径搜索与图的遍历有什么关系呢?
假设有如下一个图:
试着对这个图进行深度优先遍历并把遍历的轨迹记录下来,如下图。
遍历顺序依次为 1--->2--->6---->10---->9---->5----->13----->14----->15----->11----->12-----16---->8----->7----->3---->4,
是不是有点迷宫的样子了呢?但是上面这个经过遍历生成的路径图还不能完全算一个迷宫,因为还缺少了“墙”,补上即可成为一个完整的迷宫,如下图:
上图中蓝色的节点为墙,在遍历过程中将点之间的墙“抹掉”--即空白的格子,这样就可形成一个完整的迷宫,如上图。
怎么样,是不是就是一个迷宫了,注意到遍历一次后任意两点之间都可以找到一条路, 所以迷宫的出口和入口只需要从外围的点任选两个即可(如上图中选择的是1旁边的和16旁边的点)。
经过上面的分析,我们就知道该怎么生成一个迷宫了。
1、首先初始化迷宫的雏形,即一个矩形图阵,并为图的每个节点旁边都添加上“墙”,如下图:
2、然后对图中的非“墙”节点进行遍历(深度优先或广度优先都行),为了能够生成随机的迷宫,我们可以在遍历过程过适当的做一些随机的操作(代码中会说明),每次遍历时即打通当前遍历的点与上一个点之间的“墙”
3、遍历完所有的点即可生成一个迷宫,然后再选择出口与入口,一个完整的迷宫就形成了。
至于迷宫的路径搜索,那就完全是图的深度遍历了,大概过程如下。
(1)从迷宫起点节点V开始访问
(2)访问这个节点V,标记为可行的路径;
(3)从v的未被访问的非"墙"邻接点中选取一个顶点w,重复第二步。如果v没有未访问的非"墙"邻接点,把这个节点的可行路径标记移除,回溯至上一节点;
(4)重复上述第(2)、(3)步,直至遍历到迷宫的出口节点。
废话不多说,代码才是正道(以一个二维数组来表示迷宫):
运行结果如下图:(1表示墙,0表示通路,2表示有效路径,有点丑,有兴趣的同学可以改成图形界面版的··)
迷宫的随机生成和路径搜索主要和图的遍历有关,一般来说图的遍历主要有两种方式:1、深度优先遍历(DFS)2、广度优先遍历(BFS)
两种遍历方式都很好理解,就说说深度优先遍历:
深度优先遍历,顾名思义,就是尽可能往深处遍历,访问到一个节点时,搜索这个节点没有被访问过的相邻节点,选择一个继续做同样的操作,直到没有邻节点为止再回溯到上一个访问的节点,并选择另外的邻节点。
可以这样描述深度遍历:
(1)访问顶点v;
(2)从v的未被访问的邻接点中选取一个顶点w,重复第一步,如果v没有未访问的邻接点,回溯至上一顶点;
(3)重复上述两步,直至图中所有和v有路径相通的顶点都被访问到。
例如上图,一个可能的遍历顺序就是1-->2-->5-->6-->3-->7-->8-->4-->9-->10。
那么,迷宫的生成和路径搜索与图的遍历有什么关系呢?
假设有如下一个图:
试着对这个图进行深度优先遍历并把遍历的轨迹记录下来,如下图。
遍历顺序依次为 1--->2--->6---->10---->9---->5----->13----->14----->15----->11----->12-----16---->8----->7----->3---->4,
是不是有点迷宫的样子了呢?但是上面这个经过遍历生成的路径图还不能完全算一个迷宫,因为还缺少了“墙”,补上即可成为一个完整的迷宫,如下图:
上图中蓝色的节点为墙,在遍历过程中将点之间的墙“抹掉”--即空白的格子,这样就可形成一个完整的迷宫,如上图。
怎么样,是不是就是一个迷宫了,注意到遍历一次后任意两点之间都可以找到一条路, 所以迷宫的出口和入口只需要从外围的点任选两个即可(如上图中选择的是1旁边的和16旁边的点)。
经过上面的分析,我们就知道该怎么生成一个迷宫了。
1、首先初始化迷宫的雏形,即一个矩形图阵,并为图的每个节点旁边都添加上“墙”,如下图:
2、然后对图中的非“墙”节点进行遍历(深度优先或广度优先都行),为了能够生成随机的迷宫,我们可以在遍历过程过适当的做一些随机的操作(代码中会说明),每次遍历时即打通当前遍历的点与上一个点之间的“墙”
3、遍历完所有的点即可生成一个迷宫,然后再选择出口与入口,一个完整的迷宫就形成了。
至于迷宫的路径搜索,那就完全是图的深度遍历了,大概过程如下。
(1)从迷宫起点节点V开始访问
(2)访问这个节点V,标记为可行的路径;
(3)从v的未被访问的非"墙"邻接点中选取一个顶点w,重复第二步。如果v没有未访问的非"墙"邻接点,把这个节点的可行路径标记移除,回溯至上一节点;
(4)重复上述第(2)、(3)步,直至遍历到迷宫的出口节点。
废话不多说,代码才是正道(以一个二维数组来表示迷宫):
package test; import java.util.ArrayList; import java.util.List; import java.util.Random; import java.util.Stack; public class Maze { /** * 随机生成迷宫 * @param size * @return */ public static Point[][] createMaze(int size){ int realSize = 2 * size + 1; Point[][] maze = new Point[realSize][realSize]; //初始化迷宫雏形 for(int i = 0; i < realSize; i ++){ for(int j = 0; j < realSize; j ++){ Point p = new Point(); if(i % 2 == 0){ p.value = 1; }else{ if(j % 2 == 0){ p.value = 1; } else{ p.value = 0; } } p.x = i; p.y = j; maze[i][j] = p; } } // 保存轨迹 List<Point> trace = new ArrayList<Point>(); Point p = maze[1][1]; List<Point> tmpList = new ArrayList<Point>(); while(trace.size() < size * size){ tmpList.clear(); // 如果没有访问过,设置为访问过 if(!p.visited){ p.visited = true; // 加入到轨迹中 trace.add(p); } //依次查看上下左右四个方向的节点是否被访问过,如果没有访问过,加入列表中 // 上 if(p.x - 2 > 0) { Point upPoint = maze[p.x - 2][p.y]; if(!upPoint.visited) tmpList.add(upPoint); } // 下 if(p.x + 2 < realSize){ Point downPoint = maze[p.x + 2][p.y]; if(!downPoint.visited) tmpList.add(downPoint); } // 左 if(p.y - 2 > 0){ Point leftPoint = maze[p.x][p.y - 2]; if(!leftPoint.visited) tmpList.add(leftPoint); } // 右 if(p.y + 2 < realSize){ Point rightPoint = maze[p.x][p.y + 2]; if(!rightPoint.visited) tmpList.add(rightPoint); } // 如果上下左右还有点没有访问 if(tmpList.size() != 0){ // 随机取一个邻点,这样就可以随机生成路径达到随机生成迷宫的目的 Point nextPoint = tmpList.get(new Random().nextInt(tmpList.size())); // 打通与邻点的“墙” maze[(p.x + nextPoint.x) / 2][(p.y + nextPoint.y) / 2].value = 0; // 设置这个邻点为下一个访问点 p = nextPoint; } else{ // 如果没有邻点,从之前轨迹中的点中随机取一个,也是为了随机生成迷宫 p = trace.get(new Random().nextInt(trace.size())); } } return maze; } public static Point[][] search(Point[][] maze, Point enterPoint, Point exitPoint){ int realSize = maze.length; // 保存轨迹 Stack<Point> trace = new Stack<Point>(); // 出发点 Point p = enterPoint; boolean hasFind = false; do{ // 如果没有访问过,设置为访问过 if(!p.visited){ p.visited = true; // 设置value为2(表示可行的路径 p.value = 2; // 如果到了终点,中断 if(p == exitPoint){ hasFind = true; break; } // 加入到轨迹中 trace.push(p); } Point nextPoint = null; //依次查看上下左右四个方向的节点是否可以走 // 上 if(p.x - 1 > 0 && maze[p.x - 1][p.y].value == 0 && !maze[p.x-1][p.y].visited) { nextPoint = maze[p.x - 1][p.y]; } // 下 else if(p.x + 1 < realSize && maze[p.x + 1][p.y].value == 0 && !maze[p.x+1][p.y].visited){ nextPoint = maze[p.x + 1][p.y]; } // 左 else if(p.y - 1 > 0 && maze[p.x][p.y - 1].value == 0 && !maze[p.x][p.y - 1].visited){ nextPoint = maze[p.x][p.y - 1]; } // 右 else if(p.y + 1 < realSize && maze[p.x][p.y + 1].value == 0 && !maze[p.x][p.y + 1].visited){ nextPoint = maze[p.x][p.y + 1]; } // 如果上下左右有节点可以走 if(nextPoint != null){ // 设置这个邻点为下一个访问点 p = nextPoint; } else{ // 如果没有邻点,回退到上一个节点 // 改变value p.value = 0; p = trace.pop(); } } while(!trace.isEmpty()); if(hasFind) System.out.println("找到路径"); else System.out.println("没找到路径"); return maze; } public static void main(String[] args) { int size = 5; int realSize = size * 2 + 1; // 生成迷宫 Point[][] maze = createMaze(size); // 指定迷宫出口、入口 // 入口 Point enterPoint = maze[1][0]; // 设置入口的值为0(表示可通) enterPoint.value = 0; // 出口 Point exitPoint = maze[realSize - 2][realSize - 1]; // 设置出口的值为0(表示可通) exitPoint.value = 0; // 输出迷宫 for(int i = 0; i < realSize; i ++){ for(int j = 0; j < realSize; j ++){ System.out.print(maze[i][j].value + " "); // 重置每个点的访问性 maze[i][j].visited = false; } System.out.println(); } // 搜索出口 maze = search(maze, enterPoint, exitPoint); System.out.println(); for(int i = 0; i < realSize; i ++){ for(int j = 0; j < realSize; j ++){ System.out.print(maze[i][j].value + " "); } System.out.println(); } } static class Point{ public int x; public int y; public int value; boolean visited = false; } }
运行结果如下图:(1表示墙,0表示通路,2表示有效路径,有点丑,有兴趣的同学可以改成图形界面版的··)
相关文章推荐
- [Qt] 迷宫随机生成和寻路算法 - Qt实现的迷宫小游戏
- 随机生成图,dijkstra算法求最短路径,深度、广度优先历遍【待更新其他算法】
- 深度优先搜索迷宫路径算法的实现
- 队列实现求迷宫最短路径(包含每一步的尝试状态,迷宫随机生成)
- 利用深度优先搜索做的随机生成地图的迷宫小游戏
- 广度优先搜索迷宫路径算法的实现
- 随机生成一个迷宫并探索一条路径的JAVA实现
- 随机迷宫生成算法浅析
- C++控制台实现随机生成路径迷宫游戏
- python实现的生成随机迷宫算法核心代码分享(含游戏完整代码)
- python实现的生成随机迷宫算法核心代码分享(含游戏完整代码)
- {算法·随机生成迷宫}
- 【算法】欧拉图,欧拉回路,Eular Circuit,随机生成欧拉图,搜索欧拉回路
- 不相交类集算法生成迷宫并求解路径
- 基础算法测试——生成一个1-10之间的随机整数组合
- 迷宫路径搜索
- 一个带拐点搜索的迷宫算法
- 非有序全排列生成算法集锦
- A星路径搜索算法入门
- 一个无聊男人的疯狂《数据结构与算法分析-C++描述》学习笔记 习题2.8 随机数组的三种生成算法(补) 将bash的实现翻译成比较纯正的bash风格