LeetCode 迷宫系列(0490, 0499, 0505)

1. 迷宫1: 判断是否能走出迷宫
  1. 题目

    //由空地和墙组成的迷宫中有一个球。球可以向上下左右四个方向滚动,但在遇到墙壁前不会停止滚动。当球停下时,可以选择下一个方向。 
    //
    // 给定球的起始位置,目的地和迷宫,判断球能否在目的地停下。 
    //
    // 迷宫由一个0和1的二维数组表示。 1表示墙壁,0表示空地。你可以假定迷宫的边缘都是墙壁。起始位置和目的地的坐标通过行号和列号给出。 
    //
    // 
    //
    // 示例 1: 
    //
    // 输入 1: 迷宫由以下二维数组表示
    //
    //0 0 1 0 0
    //0 0 0 0 0
    //0 0 0 1 0
    //1 1 0 1 1
    //0 0 0 0 0
    //
    //输入 2: 起始位置坐标 (rowStart, colStart) = (0, 4)
    //输入 3: 目的地坐标 (rowDest, colDest) = (4, 4)
    //
    //输出: true
    //
    //解析: 一个可能的路径是 : 左 -> 下 -> 左 -> 下 -> 右 -> 下 -> 右。
    //
    // 
    //
    // 示例 2: 
    //
    // 输入 1: 迷宫由以下二维数组表示
    //
    //0 0 1 0 0
    //0 0 0 0 0
    //0 0 0 1 0
    //1 1 0 1 1
    //0 0 0 0 0
    //
    //输入 2: 起始位置坐标 (rowStart, colStart) = (0, 4)
    //输入 3: 目的地坐标 (rowDest, colDest) = (3, 2)
    //
    //输出: false
    //
    //解析: 没有能够使球停在目的地的路径。
    //
    // 
    //
    // 
    //
    // 注意: 
    //
    // 
    // 迷宫中只有一个球和一个目的地。 
    // 球和目的地都在空地上,且初始时它们不在同一位置。 
    // 给定的迷宫不包括边界 (如图中的红色矩形), 但你可以假设迷宫的边缘都是墙壁。 
    // 迷宫至少包括2块空地,行数和列数均不超过100。 
    // 
    // Related Topics 深度优先搜索 广度优先搜索 
    //  81  0
    
  2. 题解

    public boolean hasPath(int[][] maze, int[] start, int[] destination) {
        // 迷宫的长宽
        int row = maze.length;
        int col = maze[0].length;
    
        // 运动方向: 上, 右, 下, 左
        int[] dx = new int[]{-1, 0, 1, 0};
        int[] dy = new int[]{0, 1, 0, -1};
        // 存放转向的坐标
        int[][] visited = new int[row][col];
        Queue<int[]> queue = new LinkedList<>();
        // 将起始坐标标记为已访问, 并将其存放到队列中
        queue.add(start);
        visited[start[0]][start[1]] = 1;
        // 如果队列不为空
        while (!queue.isEmpty()) {
            // 读取队首元素
            int[] cur = queue.poll();
            // 判断队首元素是否等于终点坐标, 若等于, 则直接返回true
            if (Arrays.equals(cur, destination)) {
                return true;
            }
            // 从当前坐标向四个方向移动.
            for (int d = 0; d < 4; d++) {
                int x = cur[0];
                int y = cur[1];
                // 如果没有碰到边界, 且没有遇到墙, 则一直运行
                while (x >= 0 && x < row && y >= 0 && y < col && maze[x][y] == 0) {
                    x += dx[d];
                    y += dy[d];
                }
                // 到这里已经碰到了墙壁, 所以需要回退一步.
                x -= dx[d];
                y -= dy[d];
                // 因为只能在遇到墙的时候才能更改方向, 所以在遍历时只需要保存转变方向时的坐标, 而不能保存所有经过的点.
                // 判断当前坐标是否已被访问,
                // 如果没访问则添加的到队列, 并标记为已访问
                if (visited[x][y] == 0) {
                    queue.add(new int[]{x, y});
                    visited[x][y] = 1;
                }
            }
        }
        return false;
    }
    
2. 迷宫2: 走出迷宫的最短步数
  1. 题目

    //由空地和墙组成的迷宫中有一个球。球可以向上下左右四个方向滚动,但在遇到墙壁前不会停止滚动。当球停下时,可以选择下一个方向。 
    //
    // 给定球的起始位置,目的地和迷宫,找出让球停在目的地的最短距离。距离的定义是球从起始位置(不包括)到目的地(包括)经过的空地个数。如果球无法停在目的地,返回
    // -1。 
    //
    // 迷宫由一个0和1的二维数组表示。 1表示墙壁,0表示空地。你可以假定迷宫的边缘都是墙壁。起始位置和目的地的坐标通过行号和列号给出。 
    //
    // 
    //
    // 示例 1: 
    //
    // 输入 1: 迷宫由以下二维数组表示
    //
    //0 0 1 0 0
    //0 0 0 0 0
    //0 0 0 1 0
    //1 1 0 1 1
    //0 0 0 0 0
    //
    //输入 2: 起始位置坐标 (rowStart, colStart) = (0, 4)
    //输入 3: 目的地坐标 (rowDest, colDest) = (4, 4)
    //
    //输出: 12
    //
    //解析: 一条最短路径 : left -> down -> left -> down -> right -> down -> right。
    //             总距离为 1 + 1 + 3 + 1 + 2 + 2 + 2 = 12。
    //
    // 
    //
    // 示例 2: 
    //
    // 输入 1: 迷宫由以下二维数组表示
    //
    //0 0 1 0 0
    //0 0 0 0 0
    //0 0 0 1 0
    //1 1 0 1 1
    //0 0 0 0 0
    //
    //输入 2: 起始位置坐标 (rowStart, colStart) = (0, 4)
    //输入 3: 目的地坐标 (rowDest, colDest) = (3, 2)
    //
    //输出: -1
    //
    //解析: 没有能够使球停在目的地的路径。
    //
    // 
    //
    // 
    //
    // 注意: 
    //
    // 
    // 迷宫中只有一个球和一个目的地。 
    // 球和目的地都在空地上,且初始时它们不在同一位置。 
    // 给定的迷宫不包括边界 (如图中的红色矩形), 但你可以假设迷宫的边缘都是墙壁。 
    // 迷宫至少包括2块空地,行数和列数均不超过100。 
    // 
    // Related Topics 深度优先搜索 广度优先搜索 
    //  60  0
    
  2. 题解

    public int shortestDistance(int[][] maze, int[] start, int[] destination) {
        // 迷宫的长宽
        int row = maze.length;
        int col = maze[0].length;
        // 遍历的方向
        int[] dx = new int[]{-1, 0, 1, 0};
        int[] dy = new int[]{0, 1, 0, -1};
    
        int[][] visited = new int[row][col];
    
        // 优先队列, 按照路径长度进行升序排列
        PriorityQueue<int[]> queue = new PriorityQueue<>(new Comparator<int[]>() {
            @Override
            public int compare(int[] o1, int[] o2) {
                return o1[0] - o2[0];
            }
        });
    
        queue.add(new int[]{0, start[0], start[1]});
        while (!queue.isEmpty()) {
            int[] cur = queue.poll();
            // 如果转折点已经被访问过, 则跳过
            if (visited[cur[1]][cur[2]] == 1) {
                continue;
            }
            // 将该转折点赋值为已访问
            visited[cur[1]][cur[2]] = 1;
            // 如果当前元素的坐标等于目标坐标, 则直接返回当前元素的距离值
            if (Arrays.equals(new int[]{cur[1], cur[2]}, destination)) {
                return cur[0];
            }
            // 从四个方向遍历
            for (int d = 0; d < 4; d++) {
                int x = cur[1];
                int y = cur[2];
                int dist = cur[0];
                while (x >= 0 && x < row && y >= 0 && y < col && maze[x][y] == 0) {
                    x += dx[d];
                    y += dy[d];
                    dist++;
                }
                x -= dx[d];
                y -= dy[d];
                dist--;
                if (visited[x][y] == 0) {
                    queue.add(new int[]{dist, x, y});
                }
            }
        }
        return -1;
    }
    
3. 迷宫|||: 最短路径
  1. 题目

    //由空地和墙组成的迷宫中有一个球。球可以向上(u)下(d)左(l)右(r)四个方向滚动,但在遇到墙壁前不会停止滚动。当球停下时,可以选择下一个方向。迷宫中还有
    //一个洞,当球运动经过洞时,就会掉进洞里。 
    //
    // 给定球的起始位置,目的地和迷宫,找出让球以最短距离掉进洞里的路径。 距离的定义是球从起始位置(不包括)到目的地(包括)经过的空地个数。通过'u', 'd'
    //, 'l' 和 'r'输出球的移动方向。 由于可能有多条最短路径, 请输出字典序最小的路径。如果球无法进入洞,输出"impossible"。 
    //
    // 迷宫由一个0和1的二维数组表示。 1表示墙壁,0表示空地。你可以假定迷宫的边缘都是墙壁。起始位置和目的地的坐标通过行号和列号给出。 
    //
    // 
    //
    // 示例1: 
    //
    // 输入 1: 迷宫由以下二维数组表示
    //
    //0 0 0 0 0
    //1 1 0 0 1
    //0 0 0 0 0
    //0 1 0 0 1
    //0 1 0 0 0
    //
    //输入 2: 球的初始位置 (rowBall, colBall) = (4, 3)
    //输入 3: 洞的位置 (rowHole, colHole) = (0, 1)
    //
    //输出: "lul"
    //
    //解析: 有两条让球进洞的最短路径。
    //第一条路径是 左 -> 上 -> 左, 记为 "lul".
    //第二条路径是 上 -> 左, 记为 'ul'.
    //两条路径都具有最短距离6, 但'l' < 'u',故第一条路径字典序更小。因此输出"lul"。
    //
    // 
    //
    // 示例 2: 
    //
    // 输入 1: 迷宫由以下二维数组表示
    //
    //0 0 0 0 0
    //1 1 0 0 1
    //0 0 0 0 0
    //0 1 0 0 1
    //0 1 0 0 0
    //
    //输入 2: 球的初始位置 (rowBall, colBall) = (4, 3)
    //输入 3: 洞的位置 (rowHole, colHole) = (3, 0)
    //
    //输出: "impossible"
    //
    //示例: 球无法到达洞。
    //
    // 
    //
    // 
    //
    // 注意: 
    //
    // 
    // 迷宫中只有一个球和一个目的地。 
    // 球和洞都在空地上,且初始时它们不在同一位置。 
    // 给定的迷宫不包括边界 (如图中的红色矩形), 但你可以假设迷宫的边缘都是墙壁。 
    // 迷宫至少包括2块空地,行数和列数均不超过30。 
    // 
    // Related Topics 深度优先搜索 广度优先搜索 
    //  35  0
    
  2. 题解

    public class MazeIII {
        @Test
        public void test(){
            int[][] maze = {
                    {0, 0, 0, 0, 0},
                    {1, 1, 0, 0, 1},
                    {0, 0, 0, 0, 0},
                    {0, 1, 0, 0, 1},
                    {0, 1, 0, 0, 0}
            };
            String b = findShortestWay(maze, new int[]{4, 3}, new int[]{0, 0});
            System.out.println(b);
        }
        public String findShortestWay(int[][] maze, int[] ball, int[] hole) {
            int row = maze.length;
            int col = maze[0].length;
    
            int[] dx = new int[]{-1, 0, 1, 0};
            int[] dy = new int[]{0, 1, 0, -1};
            char[] chars = new char[]{'u', 'r', 'd', 'l'};
    
            int min_size = Integer.MAX_VALUE;
            String res = "impossible";
    
            int[][] visited = new int[row][col];
            // 优先队列, 按照dist升序排序
            PriorityQueue<Position> queue = new PriorityQueue<>(new Comparator<Position>() {
                @Override
                public int compare(Position o1, Position o2) {
                    return o1.dist - o2.dist;
                }
            });
    		// 插入起始坐标
            queue.add(new Position(ball[0], ball[1], 0, ""));
            while (!queue.isEmpty()) {
                Position cur = queue.poll();
                
                // 如果当前dist已经超过了最小距离, 则直接跳过
                if (cur.dist > min_size) {
                    continue;
                }
                
                // 如果当前位置是终点     
                if (Arrays.equals(new int[]{cur.x, cur.y}, hole)) {
                    // 如果当前dist小于最小距离, 则将最小距离替换为当前距离, 则更换路径
                    if (cur.dist < min_size) {
                        min_size = cur.dist;
                        res = cur.path;
                    } else if (cur.dist == min_size && cur.path.compareTo(res) < 0) {
                        // 如果当前dist等于最小距离, 且当前路径的字典小于最短路径的字典, 则更换路径
                        res = cur.path;
                    }
                }
    			
                // 当前坐标标记已访问
                visited[cur.x][cur.y] = 1;
                // 沿四个方向遍历
                for (int d = 0; d < 4; d++) {
                    int x = cur.x;
                    int y = cur.y;
                    int dist = cur.dist;
                    String path = cur.path;
                    path += chars[d];
                    while (x >= 0 && x < row && y >= 0 && y < col && maze[x][y] == 0) {
                        if (Arrays.equals(new int[]{x, y}, hole)) {
                            x += dx[d];
                            y += dy[d];
                            dist++;
                            break;
                        }
                        x += dx[d];
                        y += dy[d];
                        dist++;
                    }
                    x -= dx[d];
                    y -= dy[d];
                    dist--;
                    if (visited[x][y] == 0) {
                        queue.add(new Position(x, y, dist, path));
                    }
                }
            }
            return  res;
        }
    }
    
    class Position {
        int x;
        int y;
        int dist;
        String path;
    
        public Position(int x, int y, int dist, String path) {
            this.x = x;
            this.y = y;
            this.dist = dist;
            this.path = path;
        }
    }
    

你可能感兴趣的:(leetcode,迷宫)