【算法题集锦之三】--迷宫的随机生成和路径搜索

这个题目和数据结构---图有关,由于我对图掌握的不是很熟悉,所以写这篇博客来加深自己的理解。

迷宫的随机生成和路径搜索主要和图的遍历有关,一般来说图的遍历主要有两种方式: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)步,直至遍历到迷宫的出口节点。

废话不多说,代码才是正道(以一个二维数组来表示迷宫):

[java]  view plain  copy
  1. package test;  
  2.   
  3. import java.util.ArrayList;  
  4. import java.util.List;  
  5. import java.util.Random;  
  6. import java.util.Stack;  
  7.   
  8. public class Maze {  
  9.   
  10.     /** 
  11.      * 随机生成迷宫 
  12.      * @param size 
  13.      * @return 
  14.      */  
  15.     public static Point[][] createMaze(int size){  
  16.           
  17.         int realSize = 2 * size + 1;  
  18.         Point[][] maze = new Point[realSize][realSize];  
  19.         //初始化迷宫雏形  
  20.         for(int i = 0; i < realSize; i ++){  
  21.               
  22.             for(int j = 0; j < realSize; j ++){  
  23.                 Point p = new Point();  
  24.                 if(i % 2 == 0){  
  25.                       
  26.                     p.value = 1;  
  27.                       
  28.                 }else{  
  29.                       
  30.                     if(j % 2 == 0){  
  31.                           
  32.                         p.value = 1;  
  33.                           
  34.                     } else{  
  35.                           
  36.                         p.value = 0;  
  37.                           
  38.                     }  
  39.                 }  
  40.                 p.x = i;  
  41.                 p.y = j;  
  42.                 maze[i][j] = p;  
  43.             }  
  44.         }  
  45.         // 保存轨迹  
  46.         List<Point> trace = new ArrayList<Point>();  
  47.         Point p = maze[1][1];  
  48.         List<Point> tmpList = new ArrayList<Point>();  
  49.         while(trace.size() < size * size){  
  50.               
  51.             tmpList.clear();  
  52.             // 如果没有访问过,设置为访问过  
  53.             if(!p.visited){  
  54.                   
  55.                 p.visited = true;  
  56.                 // 加入到轨迹中  
  57.                 trace.add(p);  
  58.             }  
  59.             //依次查看上下左右四个方向的节点是否被访问过,如果没有访问过,加入列表中  
  60.             // 上  
  61.             if(p.x - 2 > 0) {  
  62.                   
  63.                 Point upPoint = maze[p.x - 2][p.y];  
  64.                 if(!upPoint.visited)  
  65.                     tmpList.add(upPoint);  
  66.                   
  67.             }  
  68.             // 下  
  69.             if(p.x + 2 < realSize){  
  70.                   
  71.                 Point downPoint = maze[p.x + 2][p.y];  
  72.                 if(!downPoint.visited)  
  73.                     tmpList.add(downPoint);  
  74.             }  
  75.             // 左  
  76.             if(p.y - 2 > 0){  
  77.                   
  78.                 Point leftPoint = maze[p.x][p.y - 2];  
  79.                 if(!leftPoint.visited)  
  80.                     tmpList.add(leftPoint);  
  81.             }  
  82.             // 右  
  83.             if(p.y + 2 < realSize){  
  84.                   
  85.                 Point rightPoint = maze[p.x][p.y + 2];  
  86.                 if(!rightPoint.visited)  
  87.                     tmpList.add(rightPoint);  
  88.             }  
  89.             // 如果上下左右还有点没有访问  
  90.             if(tmpList.size() != 0){  
  91.               
  92.                 // 随机取一个邻点,这样就可以随机生成路径达到随机生成迷宫的目的  
  93.                 Point nextPoint = tmpList.get(new Random().nextInt(tmpList.size()));  
  94.                 // 打通与邻点的“墙”  
  95.                 maze[(p.x + nextPoint.x) / 2][(p.y + nextPoint.y) / 2].value = 0;  
  96.                 // 设置这个邻点为下一个访问点  
  97.                 p = nextPoint;  
  98.             } else{  
  99.                 // 如果没有邻点,从之前轨迹中的点中随机取一个,也是为了随机生成迷宫  
  100.                 p = trace.get(new Random().nextInt(trace.size()));  
  101.             }  
  102.               
  103.         }  
  104.         return maze;  
  105.     }  
  106.       
  107.     public static Point[][] search(Point[][] maze, Point enterPoint, Point exitPoint){  
  108.           
  109.         int realSize = maze.length;  
  110.         // 保存轨迹  
  111.         Stack<Point> trace = new Stack<Point>();  
  112.         // 出发点  
  113.         Point p = enterPoint;  
  114.         boolean hasFind = false;  
  115.         do{  
  116.             // 如果没有访问过,设置为访问过  
  117.             if(!p.visited){  
  118.                   
  119.                 p.visited = true;  
  120.                 // 设置value为2(表示可行的路径  
  121.                 p.value = 2;  
  122.                 // 如果到了终点,中断  
  123.                 if(p == exitPoint){  
  124.                     hasFind = true;  
  125.                     break;  
  126.                 }  
  127.                 // 加入到轨迹中  
  128.                 trace.push(p);  
  129.                   
  130.             }  
  131.             Point nextPoint = null;  
  132.             //依次查看上下左右四个方向的节点是否可以走  
  133.             // 上  
  134.             if(p.x - 1 > 0 && maze[p.x - 1][p.y].value == 0 && !maze[p.x-1][p.y].visited) {  
  135.                   
  136.                 nextPoint = maze[p.x - 1][p.y];  
  137.             }  
  138.             // 下  
  139.             else if(p.x + 1 < realSize && maze[p.x + 1][p.y].value == 0 && !maze[p.x+1][p.y].visited){  
  140.                   
  141.                 nextPoint = maze[p.x + 1][p.y];  
  142.             }  
  143.             // 左  
  144.             else if(p.y - 1 > 0 && maze[p.x][p.y - 1].value == 0 && !maze[p.x][p.y - 1].visited){  
  145.                   
  146.                 nextPoint = maze[p.x][p.y - 1];  
  147.             }  
  148.             // 右  
  149.             else if(p.y + 1 < realSize && maze[p.x][p.y + 1].value == 0 && !maze[p.x][p.y + 1].visited){  
  150.                   
  151.                 nextPoint = maze[p.x][p.y + 1];  
  152.             }   
  153.             // 如果上下左右有节点可以走  
  154.             if(nextPoint != null){  
  155.                 // 设置这个邻点为下一个访问点  
  156.                 p = nextPoint;  
  157.             } else{  
  158.                 // 如果没有邻点,回退到上一个节点  
  159.                 // 改变value  
  160.                 p.value = 0;  
  161.                 p = trace.pop();  
  162.             }  
  163.               
  164.         } while(!trace.isEmpty());  
  165.         if(hasFind)  
  166.             System.out.println("找到路径");  
  167.         else  
  168.             System.out.println("没找到路径");  
  169.         return maze;  
  170.     }  
  171.     public static void main(String[] args) {  
  172.           
  173.         int size = 5;  
  174.         int realSize = size * 2 + 1;  
  175.         // 生成迷宫  
  176.         Point[][] maze = createMaze(size);  
  177.           
  178.         // 指定迷宫出口、入口  
  179.         // 入口  
  180.         Point enterPoint = maze[1][0];  
  181.         // 设置入口的值为0(表示可通)  
  182.         enterPoint.value = 0;  
  183.         // 出口  
  184.         Point exitPoint = maze[realSize - 2][realSize - 1];  
  185.         // 设置出口的值为0(表示可通)  
  186.         exitPoint.value = 0;  
  187.           
  188.         // 输出迷宫  
  189.         for(int i = 0; i < realSize; i ++){  
  190.               
  191.             for(int j = 0; j < realSize; j ++){  
  192.                   
  193.                 System.out.print(maze[i][j].value + " ");  
  194.                 // 重置每个点的访问性  
  195.                 maze[i][j].visited = false;  
  196.             }  
  197.             System.out.println();  
  198.         }  
  199.         // 搜索出口  
  200.         maze = search(maze, enterPoint, exitPoint);  
  201.         System.out.println();  
  202.         for(int i = 0; i < realSize; i ++){  
  203.               
  204.             for(int j = 0; j < realSize; j ++){  
  205.                   
  206.                 System.out.print(maze[i][j].value + " ");  
  207.             }  
  208.             System.out.println();  
  209.         }  
  210.     }  
  211.       
  212.     static class Point{  
  213.         public int x;  
  214.         public int y;  
  215.         public int value;  
  216.         boolean visited = false;  
  217.     }  
  218. }  


运行结果如下图:(1表示墙,0表示通路,2表示有效路径,有点丑,有兴趣的同学可以改成图形界面版的··)


https://blog.csdn.net/mhxy199288/article/details/37919289



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值