1. 题目
由空地(用 0 表示)和墙(用 1 表示)组成的迷宫 maze 中有一个球。球可以途经空地向 上、下、左、右 四个方向滚动,且在遇到墙壁前不会停止滚动。当球停下时,可以选择向下一个方向滚动。
- 给你一个大小为 m x n 的迷宫 maze ,以及球的初始位置 start 和目的地 destination
- 其中 start = [startrow, startcol] 且 destination = [destinationrow, destinationcol] 。
- 请你判断球能否在目的地停下:如果可以,返回 true ;否则,返回 false 。
你可以 假定迷宫的边缘都是墙壁(参考示例)。
2. 题解
(1)DFS
- 用深度优先搜索对整颗搜索树进行遍历。
- 从起始位置开始,每次选择一条路线进行搜索,并用一个二维布尔数组 visited 表示是否曾经到达过位置 (i, j)
- 若在某一次搜索到位置 (i, j) 时,visited[i, j] = true,那么我们可以进行回溯,不必继续搜索下去。
class Solution {
public boolean hasPath(int[][] maze, int[] start, int[] destination) {
int row = maze.length;
if (row == 0)
return false;
int col = maze[0].length;
if (col == 0)
return false;
boolean[][] verify = new boolean[row][col]; //标记哪些节点被遍历过
return dfs(maze, start, destination, verify);
}
public boolean dfs(int[][] maze, int[] start, int[] destination, boolean[][] verify) {
//已搜索过的值不用继续判断,直接回溯
if (verify[start[0]][start[1]])
return false;
if (start[0] == destination[0] && start[1] == destination[1])
return true;
verify[start[0]][start[1]] = true; //标记当前节点已遍历过
//定义四个方向
int up = start[0] - 1;
int down = start[0] + 1;
int left = start[1] - 1;
int right = start[1] + 1;
//向上走,传入新的start,继续找它的上下左右每个方向节点
while (up >= 0 && maze[up][start[1]] == 0)
up--;
if (dfs(maze, new int[]{up + 1, start[1]}, destination, verify)) //继续找当前节点的上下左右节点
return true; //结束 while 时,说明当前点是墙壁或者超出边界,往回走一格,up+1
//向下走
while (down < maze.length && maze[down][start[1]] == 0)
down++;
if (dfs(maze, new int[]{down - 1, start[1]}, destination, verify)) //继续找当前节点的上下左右节点
return true;
//向左走
while (left >= 0 && maze[start[0]][left] == 0)
left--;
if (dfs(maze, new int[]{start[0], left + 1}, destination, verify)) //继续找当前节点的上下左右节点
return true;
//向右走
while (right < maze[0].length && maze[start[0]][right] == 0)
right++;
if (dfs(maze, new int[]{start[0], right - 1}, destination, verify)) //继续找当前节点的上下左右节点
return true;
return false; //如果遍历所有节点都未找到终点值,返回false
}
}
简化一下代码:
class Solution {
private int[][] directions = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
//上,下,左,右
public boolean hasPath(int[][] maze, int[] start, int[] destination) {
int row = maze.length;
if (row == 0)
return false;
int col = maze[0].length;
if (col == 0)
return false;
boolean[][] verify = new boolean[row][col]; //标记哪些节点被遍历过
return dfs(maze, start, destination, verify, row, col);
}
public boolean dfs(int[][] maze, int[] start, int[] destination, boolean[][] verify, int row, int col) {
//已搜索过的值不用继续判断,直接回溯
if (verify[start[0]][start[1]])
return false;
if (start[0] == destination[0] && start[1] == destination[1])
return true;
verify[start[0]][start[1]] = true; //标记当前节点已遍历过
int x = start[0];
int y = start[1];
for (int[] direction : directions) {
int newX = x + direction[0];
int newY = y + direction[1];
while (newX >= 0 && newX < row && newY >= 0 && newY < col && maze[newX][newY] == 0) {
// 只要是空格,就一路往前走,直到碰到墙壁
newX += direction[0];
newY += direction[1];
}
//结束 while 时,说明当前点是墙壁或者超出边界,往回走一格
newX -= direction[0];
newY -= direction[1];
//这个for循环记录从起点开始能够到达的每一个节点,然后再逐一计算这些节点的每一个节点
//继续循环各个方向,如果这个停止点是曾经访问过的,则忽略
if (dfs(maze, new int[]{newX, newY}, destination, verify, row, col)) //继续找当前节点的上下左右节点
return true;
}
return false; //如果遍历所有节点都未找到终点值,返回false
}
}
(2)BFS
- 记录从起点开始能够到达的每一个节点(因为只有触碰到边界或者墙才会停下来)
- 然后再记录每一个节点的所有节点
- 判断是否有节点为目标节点,有则返回true,无则返回false;
public class Solution {
private int[][] directions = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
//上,下,左,右
public boolean hasPath(int[][] maze, int[] start, int[] destination) {
int rows = maze.length;
if (rows == 0) return false;
int cols = maze[0].length;
if (cols == 0) return false;
return bfs(maze, start, destination, rows, cols);
}
private boolean bfs(int[][] maze, int[] start, int[] destination, int rows, int cols) {
boolean[][] visited = new boolean[rows][cols]; //判断该节点是否来过
Queue<int[]> queue = new LinkedList<>(); //存储节点坐标
queue.add(start);
visited[start[0]][start[1]] = true;
while (!queue.isEmpty()) {
int[] curPoint = queue.poll(); // 获取当前坐标
int x = curPoint[0];
int y = curPoint[1];
if (x == destination[0] && y == destination[1]) {
return true;
}
for (int[] direction : directions) {
int newX = x + direction[0];
int newY = y + direction[1];
while (newX >= 0 && newX < rows && newY >= 0 && newY < cols && maze[newX][newY] == 0) {
// 只要是空格,就一路往前走,直到碰到墙壁
newX += direction[0];
newY += direction[1];
}
//结束 while 时,说明当前点是墙壁或者超出边界,往回走一格
newX -= direction[0];
newY -= direction[1];
//这个for循环记录从起点开始能够到达的每一个节点,然后再逐一计算这些节点的每一个节点
// 如果这个停止点是曾经访问过的,则忽略
if (!visited[newX][newY]) {
queue.add(new int[]{newX, newY});
visited[newX][newY] = true;
}
}
}
return false;
}
}