迷宫中离入口最近的出口 图解题解
这道题到底在问什么
- 输入
- maze=["++.+","...+","+++."], entrance=[1,2]
- 输出
- 1(相邻就有边界出口)
最优解:一步一步想明白
- 3记住「从入口一圈圈扩、入队即标记、第一次踩到边界空地即最近出口」,下面逐帧套它。
- 4入口 (1,1)(紫)入队,步数 0,并标记为已访问(以后不再走它)。规则:出口必须是边界上的空地、且不能是入口本身。开始一圈圈往外扩。
- 5弹出队首 (1,1)(紫,步数 0)。看它上下左右四个方向,能走的空地准备入队。
- 6把能走的新空地 (2,1)、(1,2)(高亮)标记已访问、带步数 1 入队。(1,1) 处理完转蓝。
- 7弹出队首 (2,1)(紫,步数 1)。看它上下左右四个方向,能走的空地准备入队。
- 8把能走的新空地 (3,1)、(2,2)(高亮)标记已访问、带步数 2 入队。(2,1) 处理完转蓝。
- 9弹出队首 (1,2)(紫,步数 1)。看它上下左右四个方向,能走的空地准备入队。
- 10把能走的新空地 (1,3)(高亮)标记已访问、带步数 2 入队。(1,2) 处理完转蓝。
- 11弹出队首 (3,1)(紫,步数 2)。看它上下左右四个方向,能走的空地准备入队。
- 12把能走的新空地 (3,2)(高亮)标记已访问、带步数 3 入队。(3,1) 处理完转蓝。
- 13弹出队首 (2,2)(紫,步数 2)。看它上下左右四个方向,能走的空地准备入队。
- 14把能走的新空地 (2,3)(高亮)标记已访问、带步数 3 入队。(2,2) 处理完转蓝。
- 15弹出队首 (1,3)(紫,步数 2)。看它上下左右四个方向,能走的空地准备入队。
- 16把能走的新空地 (1,4)(高亮)标记已访问、带步数 3 入队。(1,3) 处理完转蓝。
- 17弹出队首 (3,2)(紫,步数 3)。看它上下左右四个方向,能走的空地准备入队。
- 18把能走的新空地 (3,3)(高亮)标记已访问、带步数 4 入队。(3,2) 处理完转蓝。
- 19弹出队首 (2,3)(紫,步数 3)。看它上下左右四个方向,能走的空地准备入队。
- 20把能走的新空地 (2,4)(高亮)标记已访问、带步数 4 入队。(2,3) 处理完转蓝。
- 21弹出队首 (1,4)(紫,步数 3)。看它上下左右四个方向,能走的空地准备入队。
- 22把能走的新空地 (1,5)(高亮)标记已访问、带步数 4 入队。(1,4) 处理完转蓝。
- 23弹出队首 (3,3)(紫,步数 4)。看它上下左右四个方向,能走的空地准备入队。
- 24把能走的新空地 (3,4)(高亮)标记已访问、带步数 5 入队。(3,3) 处理完转蓝。
- 25弹出队首 (2,4)(紫,步数 4)。看它上下左右四个方向,能走的空地准备入队。
- 26把能走的新空地 (2,5)(高亮)标记已访问、带步数 5 入队。(2,4) 处理完转蓝。
- 27弹出队首 (1,5)(紫,步数 4)。看它上下左右四个方向,能走的空地准备入队。
- 28(1,5) 四周没有新的可走空地,处理完转蓝。
- 29弹出队首 (3,4)(紫,步数 5)。看它上下左右四个方向,能走的空地准备入队。
- 30把能走的新空地 (3,5)(高亮)标记已访问、带步数 6 入队。(3,4) 处理完转蓝。
- 31弹出队首 (2,5)(紫,步数 5)。看它上下左右四个方向,能走的空地准备入队。
- 32(2,5) 四周没有新的可走空地,处理完转蓝。
- 33弹出队首 (3,5)(紫,步数 6)。看它上下左右四个方向,能走的空地准备入队。
- 34往下的 (4,5) 是边界上的空地,这就是最近出口!当前步数 6 再加这一步 = 7,就是答案。
- 35沿「从谁来的」回溯,绿色就是入口到最近出口的最短路,一共 7 步。BFS 按步数逐层扩散,所以第一次碰到边界出口时的步数,一定是最近的。
⚠️ 容易写错的地方
✗ 错:把入口也当成可能的出口
✓ 对:出口必须是边界空地且不是入口
入口一入队就标记已访问,扩展时只判新格是否在边界,天然把入口排除
✗ 错:出队时才标记已访问
✓ 对:入队的那一刻就标记(改成墙)
不然同一格会被多个邻居重复入队,既慢又可能多算
✗ 错:用 DFS 找出口
✓ 对:用 BFS 按层扩散
要的是最近(最少步数),DFS 先找到的不一定最近;BFS 首次到达即最短
完整代码(Python / C++ / Java)
Python
from typing import List
from collections import deque
class Solution:
def nearestExit(self, maze: List[List[str]], entrance: List[int]) -> int:
maze = [list(row) for row in maze]
m, n = len(maze), len(maze[0])
sr, sc = entrance
q = deque([(sr, sc, 0)])
maze[sr][sc] = '+'
while q:
r, c, d = q.popleft()
for dr, dc in ((1,0),(-1,0),(0,1),(0,-1)):
nr, nc = r + dr, c + dc
if 0 <= nr < m and 0 <= nc < n and maze[nr][nc] == '.':
if nr == 0 or nr == m - 1 or nc == 0 or nc == n - 1:
return d + 1
maze[nr][nc] = '+'
q.append((nr, nc, d + 1))
return -1C++
#include <queue>
#include <vector>
using namespace std;
class Solution {
public:
int nearestExit(vector<vector<char>>& maze, vector<int>& entrance) {
int m = maze.size(), n = maze[0].size();
queue<array<int,3>> q;
q.push({entrance[0], entrance[1], 0});
maze[entrance[0]][entrance[1]] = '+';
int dirs[5] = {1,0,-1,0,1};
while (!q.empty()) {
auto [r, c, dist] = q.front(); q.pop();
for (int d = 0; d < 4; ++d) {
int nr = r + dirs[d], nc = c + dirs[d+1];
if (nr >= 0 && nr < m && nc >= 0 && nc < n && maze[nr][nc] == '.') {
if (nr == 0 || nr == m - 1 || nc == 0 || nc == n - 1) return dist + 1;
maze[nr][nc] = '+';
q.push({nr, nc, dist + 1});
}
}
}
return -1;
}
};Java
import java.util.*;
class Solution {
public int nearestExit(char[][] maze, int[] entrance) {
int m = maze.length, n = maze[0].length;
Queue<int[]> q = new ArrayDeque<>();
q.offer(new int[]{entrance[0], entrance[1], 0});
maze[entrance[0]][entrance[1]] = '+';
int[] dirs = {1,0,-1,0,1};
while (!q.isEmpty()) {
int[] cur = q.poll();
for (int d = 0; d < 4; d++) {
int nr = cur[0] + dirs[d], nc = cur[1] + dirs[d + 1];
if (nr >= 0 && nr < m && nc >= 0 && nc < n && maze[nr][nc] == '.') {
if (nr == 0 || nr == m - 1 || nc == 0 || nc == n - 1) return cur[2] + 1;
maze[nr][nc] = '+';
q.offer(new int[]{nr, nc, cur[2] + 1});
}
}
}
return -1;
}
}复杂度
时间
O(mn)
每个格子最多入队一次、出队一次,各看四个方向,共 O(mn)
空间
O(mn)
队列与访问标记最坏装下整张迷宫,O(mn)
看不够?换成动画再走一遍
上面的推演每一步都对应一帧动画。点开交互动画版,能一步步看着 迷宫中离入口最近的出口 的数据怎么变、指针怎么走,还能切 Python / Java / C++ 跟着练。
面试官可能追问
能不能从四条边界的所有空地反过来做多源 BFS?+
可以,而且常更快:把所有边界出口(边界上的空地、且坐标不等于入口)当成多个源点一起入队(步数 0),向内扩散,第一次扩到入口时的步数就是答案。关键是入口若恰好在边界上,必须从源点集合里剔除,否则会把入口本身当出口、错误得到 0。多源 BFS 在「出口很多、入口单一」时省去重复探索。本题单入口正向 BFS 也够用,两种都对。
如果迷宫非常大、内存吃紧怎么办?+
可以用双向 BFS:从入口和从边界出口集合(同样要排除入口自身)同时扩展,相遇时步数相加。或者原地复用 maze 数组当访问标记(本题正是把走过的格改成 +),省掉额外的 visited 矩阵,空间常数更小。
想听吴师兄把这道题讲给你听?
文字版和动画都随便看。开通图解算法年卡,可以听吴师兄声线把 迷宫中离入口最近的出口 一步步讲透(全站 0 节语音精讲,持续扩充), 卡住的地方还有 AI 私教小欧就着动画帮你拆到懂。
本平台为独立第三方培训机构,与华为技术有限公司无任何关联;课程的服务内容与权益以购买协议为准,学习效果因个人情况而异。「华为 OD」「华为可信」等仅为对岗位与考试方向的客观描述,相关商标归各自权利人所有。