Java实现 _迷宫中离入口最近的出口_BFS(❗❗❗经典面试题❗❗❗)

426 阅读4分钟

题目链接:leetcode.cn/problems/ne…

题目描述:

给你一个 m x n 的迷宫矩阵 maze下标从 0 开始),矩阵中有空格子(用 '.' 表示)和墙(用 '+' 表示)。同时给你迷宫的入口 entrance ,用 entrance = [entrancerow, entrancecol] 表示你一开始所在格子的行和列。

每一步操作,你可以往 或者 移动一个格子。你不能进入墙所在的格子,你也不能离开迷宫。你的目标是找到离 entrance 最近 的出口。出口 的含义是 maze 边界 上的 空格子entrance 格子 不算 出口。

请你返回从 entrance 到最近出口的最短路径的 步数 ,如果不存在这样的路径,请你返回 -1

示例 1:

img

 输入:maze = [["+","+",".","+"],[".",".",".","+"],["+","+","+","."]], entrance = [1,2]
 输出:1
 解释:总共有 3 个出口,分别位于 (1,0),(0,2) 和 (2,3) 。
 一开始,你在入口格子 (1,2) 处。
 - 你可以往左移动 2 步到达 (1,0) 。
 - 你可以往上移动 1 步到达 (0,2) 。
 从入口处没法到达 (2,3) 。
 所以,最近的出口是 (0,2) ,距离为 1 步。

示例 2:

img

 输入:maze = [["+","+","+"],[".",".","."],["+","+","+"]], entrance = [1,0]
 输出:2
 解释:迷宫中只有 1 个出口,在 (1,2) 处。
 (1,0) 不算出口,因为它是入口格子。
 初始时,你在入口与格子 (1,0) 处。
 - 你可以往右移动 2 步到达 (1,2) 处。
 所以,最近的出口为 (1,2) ,距离为 2 步。

示例 3:

img

 输入:maze = [[".","+"]], entrance = [0,0]
 输出:-1
 解释:这个迷宫中没有出口。

提示:

  • maze.length == m
  • maze[i].length == n
  • 1 <= m, n <= 100
  • maze[i][j] 要么是 '.' ,要么是 '+'
  • entrance.length == 2
  • 0 <= entrancerow < m
  • 0 <= entrancecol < n
  • entrance 一定是空格子。

解析

如果使用FloodFill算法解决了图像渲染的问题,那么再来看这题其实也不难。

在做图像渲染问题的时候不知道大家有没有考虑过重复渲染的问题,在那题中题目有一个条件其实很重要,就是被修改过后的颜色,如果一个坐标的颜色已经被修改过了,那么就代表着这个坐标“这条路” )已经走过了,所以在那题中我们在入队列的时候需要加一个判断条件,就是这个颜色必须为待修改的颜色。

如果把上述的思想投入到这题的话,就是我们需要有东西来记录这个格子我走过没,如果走过了,我就不能再重复走了。这是这题算法的第一个思想。那么我们在写代码的时候直接创建一个和迷宫一样大的二维数组就可以了,如果这个格子走过了,就将对应的位置赋值为1。

第二个问题,就是走的步数应该怎样地递增才是正确的,是走一个格子就算一步吗?那这样走不同的道路的步数都被加起来了,肯定是不对的。应该是往外扩一层的时候步数加一才是正确的。也就是需要我们在入队列的时候先将 这一层的 格子取出,然后分别遍历格子的上下左右位置,看是否满足出口条件,不满足的话再看看是否满足入队列的条件。

那么有同学就说, “这一层” 应该怎么定义呢,其实就是你往外扩的方式,也就是这个格子的 “上下左右” 位置。

以上就是大概的思路,更具体的细节,我们看代码。

代码

import java.util.LinkedList;
import java.util.Queue;
class Solution {
    /*和图像渲染一样的,我们先把上下左右的对应坐标需要加的数字放到数组中,待会方便遍历
    这样写的好处就是待会不用写四个if判断条件,当然如果你有更好的方法也欢迎在评论区留言讨论
    */
    int[] dx = {0, 0, 1, -1};
    int[] dy = {1, -1, 0, 0};
    public int nearestExit(char[][] maze, int[] entrance) {
        Queue<int[]> queue = new LinkedList<>();
        //先获取到二维数组长度
        int m = maze.length;
        int n = maze[0].length;
        
        //将入口添加到queue中
        queue.add(new int[]{entrance[0],entrance[1]});
        
        //定义一个相同的二维数组用来标记“走过的路”,也就是格子
        int[][] arr = new int[m][n];
        
        //然后将入口的位置设置为1,因为入口位置已经走过了
        arr[entrance[0]][entrance[1]] = 1;
        
        //步数
        int step = 0;
        while (!queue.isEmpty()) {
            //每走完一层将步数加一
            step++;
            //获取当前queue.size(),也就是这一层的不同路径数,开始遍历
            int size = queue.size();
            for (int i = 0; i < size; i++) {
                //取出坐标
                int[] cur = queue.poll();
                int x = cur[0];
                int y = cur[1];
                //然后再对这个坐标往外扩一层
                for (int j = 0; j < 4; j++) {
                    int x1 = x + dx[j];
                    int y1 = y + dy[j];
                    //开始判断这个坐标是否满足未走过且不是墙的条件
                    if (x1 >= 0 && x1 < m && y1 >= 0 && y1 < n && arr[x1][y1] == 0 && maze[x1][y1] == '.') {
                        //再判断是否为出口
                        if (x1 == 0 || x1 == m - 1 || y1 == 0 || y1 == n - 1) {
                            //为出口直接返回步数
                            return step;
                        } else {
                            //不是出口添加到queue中。
                            queue.add(new int[]{x1,y1});
                            //将走过的这个路径赋值为1
                            arr[x1][y1] = 1;
                        }
                    }
                }
            }
        }
        //while循环走完还没有返回step的话就是没有出口
        return -1;
    }
}