问题描述:这时实验心理学中的一个典型的问题,心理学家吧一只老鼠从一个无顶的大盒子的入口处赶进迷宫。迷宫设置很多隔壁,对前进方向形成了许多障碍,心理学家在迷宫的唯一出口处放置了一块奶酪,吸引老鼠仔迷宫中寻找通路以到达出口。
求解思想:回溯法是一种不断试探且及时纠正错误的搜索方法,下面的求解过程采用回溯法。从入口出发,按某一方向向前探索,若能走通(未走过的),即某处可以到达,则到达一个新点,否则试探下一个方向;若所有的方向均没有通路,则沿原路返回前一点,换下一个方向继续试探,直到所有可能的通路都搜索到,或找到一条通路,或无路可走又返回到入口点。这里可以用一个栈来实现,每走一步,将该位置压入栈中,若该点无路可走,则出栈返回上一位置。
需要解决的四个问题:
(1)表示迷宫的数据结构
设迷宫为m行n列,利用数组maze[m][n]来表示一个迷宫,maze[i][j]=0或1,其中0表示通路,1表示不通。迷宫该数组四边都为1,代表迷宫四周都是墙。这样就可以保证每个点都有8个方向可以试探。
入口为(1,1),出口为(6,8)
1,1,1,1,1,1,1,1,1,1
1,0,1,1,1,0,1,1,1,1
1,1,0,1,0,1,1,1,1,1
1,0,1,0,0,0,0,0,1,1
1,0,1,1,1,0,1,1,1,1
1,1,0,0,1,1,0,0,0,1
1,0,1,1,0,0,1,1,0,1
1,1,1,1,1,1,1,1,1,1
(2)试探方向
迷宫中间每个点都有8个方向可以试探。其增量数组可以用一个1*2的二维数组move表述,具体值如下:
x y
0 0 1
1 1 1
2 1 0
3 1 -1
4 0 -1
5 -1 -1
6 -1 0
7 -1 1
在move数组中,x表示横坐标的增量,y表示纵坐标的增量。
(3)栈中存放元素的设计
栈中所存放的元素应该包含所到达的每点的坐标以及从该点沿哪个方向向下走的。可以用一个类表示:
1 class Step{ 2 int x,y,d; 3 public Step(int x,int y,int d) { 4 this.x = x;//横坐标 5 this.y = y;//纵坐标 6 this.d = d;//方向 7 } 8 }
(4)防止重复到达某点而发生死循环
可以用两种方法来实现,第一种就是另外设置一个标志数组mark[m][n],它的所有元素初始都为0,一旦到达某点,则设置该点的标志位1,下次试探的时候就不再走这点了。第二种就是当到达某点的时候,使maze[i][j]置为-1,以便区别为达到的点,同样也可以防止走重复点的作用。
具体代码如下:
1 package com.stack; 2 3 import java.util.Stack; 4 5 /** 6 * 用栈来实现迷宫的求解 7 * @author 刘玲 8 * 9 */ 10 11 class Step{ 12 int x,y,d; 13 public Step(int x,int y,int d) { 14 this.x = x;//横坐标 15 this.y = y;//纵坐标 16 this.d = d;//方向 17 } 18 } 19 20 public class MazeTest { 21 22 public static void main(String[] args) { 23 // 迷宫定义 24 int[][] maze = {{1,1,1,1,1,1,1,1,1,1}, 25 {1,0,1,1,1,0,1,1,1,1}, 26 {1,1,0,1,0,1,1,1,1,1}, 27 {1,0,1,0,0,0,0,0,1,1}, 28 {1,0,1,1,1,0,1,1,1,1}, 29 {1,1,0,0,1,1,0,0,0,1}, 30 {1,0,1,1,0,0,1,1,0,1}, 31 {1,1,1,1,1,1,1,1,1,1}}; 32 int[][] move = {{0,1},{1,1},{1,0},{1,-1},{0,-1},{-1,-1},{-1,0},{-1,1}}; 33 Stack<Step> s = new Stack<Step>(); 34 Stack<Integer> s1 = new Stack<Integer>(); 35 int a = path(maze, move, s); 36 while(!s.isEmpty()){ 37 Step step = s.pop(); 38 System.out.println(step.x+":"+step.y); 39 } 40 } 41 public static int path(int[][] maze,int[][] move,Stack<Step> s){ 42 Step temp = new Step(1,1,-1); //起点 43 s.push(temp); 44 while(!s.isEmpty()){ 45 temp = s.pop(); 46 int x = temp.x; 47 int y = temp.y; 48 int d = temp.d+1; 49 while(d<8){ 50 int i = x + move[d][0]; 51 int j = y + move[d][1]; 52 if(maze[i][j] == 0){ //该点可达 53 temp = new Step(i,j,d); //到达新点 54 s.push(temp); 55 x = i; 56 y = j; 57 maze[x][y] = -1; //到达新点,标志已经到达 58 if(x == 6 && y == 8){ 59 return 1; //到达出口,迷宫有路,返回1 60 }else{ 61 d = 0; //重新初始化方向 62 } 63 }else{ 64 d++; //改变方向 65 } 66 } 67 } 68 return 0; 69 } 70 71 }
输出结果如下:
6:8
5:8
5:7
5:6
4:5
3:5
3:4
3:3
2:2
由于栈是后进先出的,所以结果应该从下面看起(2,2)->(3,3)->(3,4)->(3,5)->(4,5)->(5,6)->(5,7)->(5,8)->(6,8)
有运行结果可以看出来,栈中所存储的就是迷宫的一条通路。
上面那个代码有一点点小问题,非常感谢问题的提出者,修改如下:
1 package com.stack; 2 3 import java.util.Stack; 4 5 /** 6 * 用栈来实现迷宫的求解 7 * @author 刘玲 8 * 9 */ 10 11 class Step{ 12 int x,y,d; 13 public Step(int x,int y,int d) { 14 this.x = x;//横坐标 15 this.y = y;//纵坐标 16 this.d = d;//方向 17 } 18 } 19 20 public class MazeTest { 21 22 public static void main(String[] args) { 23 // 迷宫定义 24 int[][] maze = {{1,1,1,1,1,1,1,1,1,1}, 25 {1,0,1,1,1,0,1,1,1,1}, 26 {1,1,0,1,0,1,1,1,1,1}, 27 {1,0,1,0,0,0,0,0,1,1}, 28 {1,0,1,1,1,0,1,1,1,1}, 29 {1,1,0,0,1,1,0,0,0,1}, 30 {1,0,1,1,0,0,1,1,0,1}, 31 {1,1,1,1,1,1,1,1,1,1}}; 32 int[][] move = {{0,1},{1,1},{1,0},{1,-1},{0,-1},{-1,-1},{-1,0},{-1,1}}; 33 Stack<Step> s = new Stack<Step>(); 34 int a = path(maze, move, s); 35 while(!s.isEmpty()){ 36 Step step = s.pop(); 37 System.out.println(step.x+":"+step.y); 38 } 39 } 40 public static int path(int[][] maze,int[][] move,Stack<Step> s){ 41 Step temp = new Step(1,1,-1); //起点 42 s.push(temp); 43 while(!s.isEmpty()){ 44 //temp = s.pop(); 这样写是有问题的,不能将出栈的元素作为当前位置,应该将栈顶元素作为当前位置 45 s.pop(); //如果路走不通就出栈 46 if(!s.isEmpty()){ 47 temp = s.peek(); //将栈顶元素设置为当前位置 48 } 49 int x = temp.x; 50 int y = temp.y; 51 int d = temp.d+1; 52 while(d<8){ 53 int i = x + move[d][0]; 54 int j = y + move[d][1]; 55 if(maze[i][j] == 0){ //该点可达 56 temp = new Step(i,j,d); //到达新点 57 s.push(temp); 58 x = i; 59 y = j; 60 maze[x][y] = -1; //到达新点,标志已经到达 61 if(x == 6 && y == 1){ 62 return 1; //到达出口,迷宫有路,返回1 63 }else{ 64 d = 0; //重新初始化方向 65 } 66 }else{ 67 d++; //改变方向 68 } 69 } 70 } 71 return 0; 72 } 73 74 }