【面试】如何找到迷宫出口
一、问题描述
给出如下的矩阵
1 1 1 1
0 0 0 1
1 9 0 0
1 1 1 1
其中1代表此位置是可以通过的,0代表此位置是不可通过的,要从起点开始,确定是否存在这样一条路径,可以从起点找到数字9。也就是找到这样一条路径1->1->1 ...... ->9。这个问题是寻找迷宫出口的变形。可以将9看成是迷宫的出口,而给出的开始位置是迷宫的入口,寻找一条路径。
二、问题求解
当矩阵阶数不是太大的时,可以使用递归来求解,但是,当矩阵的阶数变得很大时,则需要使用另外的方法进行计算,也就是采用栈来进行求解。
下面是两种解决方法
1.当矩阵阶数较小时,采用递归进行求解,这种方式只是用来开拓思路,解此问题存在一定的局限性。
源代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | import java.util.Scanner; public class MatrixPath { public static void main(String[] args) { Scanner scan = new Scanner(System.in); String line = scan.nextLine(); //输入行和列 String[] lines = line.split( "\\s+" ); //分离行和列 int row = Integer.parseInt(lines[ 0 ]); int col = Integer.parseInt(lines[ 1 ]); int [][] matrix = new int [row + 2 ][col + 2 ]; //将外层包围一层0元素 footPrints = new boolean [row + 2 ][col + 2 ]; endPrints = new boolean [row + 2 ][col + 2 ]; for ( int i = 0 ; i < row; i++) { //初始化数组元素 line = scan.nextLine(); lines = line.split( "\\s" ); for ( int j = 0 ; j < lines.length; j++) { matrix[i + 1 ][j + 1 ] = Integer.parseInt(lines[j]); } } scan.close(); int startX = 1 ; //入口横坐标 int startY = 1 ; //入口纵坐标 System.out.println(exsitPath(startX, startY, row, col, matrix) + " " ); } public static boolean exsitPath( int x, int y, int row, int col, int [][] matrix) { if (x < 0 || y < 0 || x >= row || y >= col) return false ; if (matrix[x][y] == 9 ) return true ; if (matrix[x][y] == 0 ) return false ; if (exsitPath(x + 1 , y, row, col, matrix) || exsitPath(y - 1 , x, row, col, matrix) || exsitPath(x, y - 1 , row, col, matrix) || exsitPath(x, y + 1 , row, col, matrix)) return true ; return false ; } } |
测试用例:
3 3
1 1 1
1 9 1
0 1 1
输出结果为:true
测试用例:
4 4
1 1 1 1
0 0 0 1
0 0 1 1
9 1 1 1
输出结果:java.lang.StackOverflowError,堆栈溢出
2.使用递归的方法要选择好测试用例,很可能因为测试用例的选取不恰当而造成堆栈溢出。递归只是为了开拓思路而已,下面使用栈结构来解决堆栈溢出的问题。
源代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 | package com.leesf.Main; import java.util.ArrayList; import java.util.Scanner; class Position { //位置信息 private int x; private int y; public Position( int x, int y) { this .x = x; this .y = y; } public int getX() { return x; } public int getY() { return y; } } class Item { //栈中元素类型 private int order; //第几步 private Position seat; //位置 private int value; //对应的值 private int di; //方位 public Item( int order, Position seat, int value, int di) { this .order = order; this .seat = seat; this .value = value; this .di = di; } public int getOrder() { return order; } public Position getSeat() { return seat; } public int getDi() { return di; } public int getValue() { return value; } public void setDi( int di) { this .di = di; } } class MyStack<T> { //定义栈结构 private static int index = - 1 ; private ArrayList<T> array; public MyStack() { array = new ArrayList<T>(); } public void push(T item) { array.add(item); index++; } public T pop() { T result = array.get(index); array.remove(index); index--; return result; } public boolean isEmpty() { if (index != - 1 ) return false ; else return true ; } } public class MatrixPath { private static boolean [][] footPrints; //留下脚印 private static boolean [][] endPrints; //留下不能通过印记 public static void main(String[] args) { Scanner scan = new Scanner(System.in); String line = scan.nextLine(); //输入行和列 String[] lines = line.split( "\\s+" ); //分离行和列 int row = Integer.parseInt(lines[ 0 ]); int col = Integer.parseInt(lines[ 1 ]); int [][] matrix = new int [row + 2 ][col + 2 ]; //将外层包围一层0元素 footPrints = new boolean [row + 2 ][col + 2 ]; endPrints = new boolean [row + 2 ][col + 2 ]; for ( int i = 0 ; i < row; i++) { //初始化数组元素 line = scan.nextLine(); lines = line.split( "\\s" ); for ( int j = 0 ; j < lines.length; j++) { matrix[i + 1 ][j + 1 ] = Integer.parseInt(lines[j]); } } scan.close(); int startX = 1 ; //入口横坐标 int startY = 1 ; //入口纵坐标 System.out.println(exsitPath(startX, startY, row, col, matrix) + " " ); //System.out.println(exsitPath(startX, startY, matrix) + " "); } //判断是否存在路径 public static boolean exsitPath( int x, int y, int [][] matrix) { MyStack<Item> stack = new MyStack<Item>(); //当前的位置 Position curPos = new Position(x, y); //开始第一步 int curStep = 1 ; do { if (pass(curPos, matrix)) { //该位置是可通过的 System.out.println( "x - y = " + curPos.getX() + " - " + curPos.getY()); footPrint(curPos); //留下脚印 Item item = new Item(curStep, curPos, matrix[curPos.getX()][curPos.getY()], 1 ); stack.push(item); //将该可通位置放入栈中 if (item.getValue() == 9 ) { //如果已经找到目标,则返回 System.out.println( "总共花费了" + curStep + "步, 找到目标" ); return true ; } curPos = nextPos(curPos, 1 ); //试探下一步,即东边的,但是不放入栈中 curStep++; //步数加1 } else { //改位置不可通过 if (!stack.isEmpty()) { //栈不为空 Item item = stack.pop(); //出栈 while (item.getDi() == 4 && !stack.isEmpty()) { //留下了脚印的个数加上不可通过的个数之和为4,进行出栈处理 markPrint(item.getSeat()); //留下不可通过印记并进行出栈处理 item = stack.pop(); //出栈 } if (item.getDi() < 4 ) { //四个方向还未试探完 item.setDi(item.getDi() + 1 ); //换下一个方向进行试探 stack.push(item); //入栈 curPos = nextPos(item.getSeat(), item.getDi()); //试探下一个位置 } } } } while (!stack.isEmpty()); System.out.println( "总共花费了" + curStep + "步,没有找到目标" ); return false ; } public static boolean pass(Position curPos, int [][] matrix) { if (matrix[curPos.getX()][curPos.getY()] != 0 && endPrints[curPos.getX()][curPos.getY()] == false && footPrints[curPos.getX()][curPos.getY()] == false ) return true ; else return false ; } public static void footPrint(Position curPos) { footPrints[curPos.getX()][curPos.getY()] = true ; } public static Position nextPos(Position curPos, int di) { int x = curPos.getX(); int y = curPos.getY(); switch (di) { case 1 : { y = curPos.getY() + 1 ; //东 } break ; case 2 : { x = curPos.getX() + 1 ; //南 } break ; case 3 : { y = curPos.getY() - 1 ; //西 } break ; case 4 : { x = curPos.getX() - 1 ; //北 } break ; } return new Position(x, y); } public static void markPrint(Position seat) { endPrints[seat.getX()][seat.getY()] = true ; } } |
测试用例:
4 4
1 1 1 1
0 0 0 1
0 0 1 1
9 1 1 1
输出结果:
x - y = 1 - 1
x - y = 1 - 2
x - y = 1 - 3
x - y = 1 - 4
x - y = 2 - 4
x - y = 3 - 4
x - y = 4 - 4
x - y = 4 - 3
x - y = 4 - 2
x - y = 4 - 1
总共花费了10步, 找到目标
true
测试用例:
8 8
1 1 0 0 0 0 1 1
1 1 0 0 0 0 0 0
0 1 1 1 1 0 0 0
1 1 1 1 0 0 0 0
0 1 0 0 1 1 1 1
0 1 1 9 0 0 0 0
0 0 0 0 0 0 0 0
1 0 1 0 1 1 1 1
输出结果:
x - y = 1 - 1
x - y = 1 - 2
x - y = 2 - 2
x - y = 3 - 2
x - y = 3 - 3
x - y = 3 - 4
x - y = 3 - 5
x - y = 4 - 4
x - y = 4 - 3
x - y = 4 - 2
x - y = 5 - 2
x - y = 6 - 2
x - y = 6 - 3
x - y = 6 - 4
总共花费了14,步, 找到目标
true
测试用例:
4 4
1 0 0 1
1 1 1 1
0 0 1 0
1 1 1 1
输出结果:
x - y = 1 - 1
x - y = 2 - 1
x - y = 2 - 2
x - y = 2 - 3
x - y = 2 - 4
x - y = 1 - 4
x - y = 3 - 3
x - y = 4 - 3
x - y = 4 - 4
x - y = 4 - 2
x - y = 4 - 1
总共花费了12步,没有找到目标
false
三、总结
两种方法完成了问题的解决,当然,递归的方法只是用来扩展思路,其实,使用递归并且记录之前已经遍历到一些信息也是可以完成问题的解答,这就涉及到了动态规划。这就交给读者去完成啦。
好了,谢谢各位观看,我们下期再会。
PS:如果您觉得阅读本文对您有帮助,请点一下“推荐”按钮,您的“推荐”,将会是我不竭的动力!
作者:leesf 掌控之中,才会成功;掌控之外,注定失败。
出处:http://www.cnblogs.com/leesf456/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
如果觉得本文对您有帮助,您可以请我喝杯咖啡!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!