leetcode
leetcode 451 ~ 500
迷宫 III

迷宫 III

难度:

标签:

题目描述

代码结果

运行时间: 33 ms, 内存: 16.3 MB


/*
 * 思路:
 * 1. 使用广度优先搜索(BFS)在迷宫中寻找从起点到终点的最短路径。
 * 2. 利用Java Streams来处理优先队列中的元素。
 * 3. 路径信息通过字符串的形式累积,优先选择字典序小的路径。
 */
import java.util.*;
import java.util.stream.Stream;
 
public class SolutionStream {
    private static final int[][] DIRECTIONS = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
    private static final char[] DIR_CHARS = {'u', 'd', 'l', 'r'};
 
    public String findShortestWay(int[][] maze, int[] ball, int[] hole) {
        int m = maze.length, n = maze[0].length;
        PriorityQueue<Point> pq = new PriorityQueue<>((a, b) -> a.dist == b.dist ? a.path.compareTo(b.path) : a.dist - b.dist);
        pq.offer(new Point(ball[0], ball[1], 0, ""));
        boolean[][] visited = new boolean[m][n];
        visited[ball[0]][ball[1]] = true;
 
        while (!pq.isEmpty()) {
            Point p = pq.poll();
            if (p.x == hole[0] && p.y == hole[1]) return p.path;
 
            for (int i = 0; i < DIRECTIONS.length; i++) {
                int[] dir = DIRECTIONS[i];
                int x = p.x, y = p.y, dist = p.dist;
                while (x + dir[0] >= 0 && x + dir[0] < m && y + dir[1] >= 0 && y + dir[1] < n && maze[x + dir[0]][y + dir[1]] == 0) {
                    x += dir[0];
                    y += dir[1];
                    dist++;
                    if (x == hole[0] && y == hole[1]) break;
                }
                if (visited[x][y]) continue;
                visited[x][y] = true;
                pq.offer(new Point(x, y, dist, p.path + DIR_CHARS[i]));
            }
        }
        return "impossible";
    }
 
    private static class Point {
        int x, y, dist;
        String path;
 
        Point(int x, int y, int dist, String path) {
            this.x = x;
            this.y = y;
            this.dist = dist;
            this.path = path;
        }
    }
}
 

解释

方法:

该题解使用了Dijkstra算法来寻找从起点到终点的最短路径。与普通的Dijkstra算法不同的是,除了记录最短距离外,还需要记录到达每个位置的路线。具体实现时,使用优先队列来存储当前探索到的位置,每次取出距离最小的位置进行扩展。对于每个位置,尝试向四个方向移动,直到撞墙或到达终点。如果到达一个之前未探索过的位置,则更新其距离并记录路线;如果到达一个已探索过的位置,且距离相等,则在该位置的路线中追加新的路线。最后,如果到达终点,则返回字典序最小的路线;如果无法到达终点,则返回'impossible'。

时间复杂度:

O(mnlog(mn))

空间复杂度:

O(mn(m+n))

代码细节讲解

🦆
在使用Dijkstra算法时,你是如何处理迷宫中的'撞墙'逻辑?在代码中似乎有回退一步的操作,请问这是怎样实现的?
在实现迷宫问题的Dijkstra算法中,处理撞墙的逻辑是通过在移动过程中检查下一个移动的格子是否有效(即是否在迷宫范围内且不是墙)。当沿着某一方向前进且即将移动到的格子是墙时(或者超出迷宫边界),算法会停止在当前方向的前进,并进行回退一步的操作。具体地,在代码中,这一逻辑是通过在循环条件判断中将移动到的下一个位置设为墙或出界前的最后一个合法格子实现的。这意味着一旦发现下一个位置不合法,循环会结束,此时的坐标还是合法的最后位置,但在循环结束后,代码会减去最后一次的移动(dx或dy),使(x, y)回退到最后一个合法的位置。
🦆
此题解中提到,如果到达一个已探索过的位置且距离相等,则在该位置的路线中追加新的路线。请问这种处理对于Dijkstra算法的效率有何影响?
在Dijkstra算法中,当发现一条到达某个位置的相同长度的新路径时,将这条路径加入到该位置的路径列表中,这种处理方式确实会增加算法的空间复杂度(因为需要存储额外的路径信息)。此外,每次向优先队列中添加相同距离的新路径,也会稍微增加时间复杂度,因为优先队列需要对更多的元素进行排序。然而,这种影响通常是可接受的,因为它使得我们能够探索所有可能的最短路径,进而可以在最后选择字典序最小的路径,这在某些应用场景下是有用的。
🦆
题解中提到当到达终点时,返回字典序最小的路线。请问是否有可能存在多条相同长度但字典序不同的最短路径?如果有,该如何决定返回哪一条?
是的,存在可能有多条相同长度但字典序不同的最短路径的情况。在这种情况下,题解中通过维护一个路径列表,并在到达终点时从这些路径中选择一个字典序最小的路径来返回。这通过在最终到达终点时,比较存储在路径列表中的所有路径,选择出字典序最小的一个实现。这种方法确保了无论有多少条相同长度的路径,总能返回一个确定且最优的结果。
🦆
在更新距离数组和路径字典时,如果新计算的距离小于之前记录的距离,会替换原有的路径。这种操作是否可能导致先前的一些优秀路径被遗漏?
在Dijkstra算法中,如果一个位置的新计算距离小于已记录的距离,则意味着我们找到了一条更短的路径到达该位置,因此会更新该位置的距离并替换原有的路径。这种操作不会导致遗漏‘优秀’路径,因为所谓的优秀路径应当是指更短的路径。算法的目标是找到从起点到终点的最短路径,因此,只有更短的路径才被考虑为“优秀”,而更长的路径即使在字典序上较好也会被舍弃。这是因为我们的首要目标是最小化路径长度,而非路径的字典序。

相关问题

迷宫

迷宫

迷宫 II

迷宫 II