题解 | #走迷宫#
走迷宫
https://www.nowcoder.com/practice/e88b41dc6e764b2893bc4221777ffe64
题目链接
题目描述
在一个 的网格中,你从起点
出发,每次可以向上、下、左、右移动一步。某些格子是障碍物,无法通过。你需要计算从起点移动到终点
的最少步数。如果无法到达,则输出 -1。
解题思路
这是一个典型的在二维网格中寻找最短路径的问题。由于每次移动的成本(步数)都是 1,这是一个无权图的最短路径问题。解决此类问题的最佳算法是广度优先搜索 (Breadth-First Search, BFS)。
BFS 的核心思想是从起点开始,逐层向外进行“地毯式”搜索。它首先访问所有距离起点为 1 的节点,然后是所有距离为 2 的节点,以此类推。由于这种逐层扩展的特性,当 BFS 第一次到达终点时,所经过的路径必然是步数最少的。
算法步骤如下:
-
数据结构:
- 一个队列,用于存储待访问的节点。队列中的每个元素需要包含节点的状态,即坐标
和到达该坐标的步数
。
- 一个二维的距离数组
dist[N][M]
,用于记录从起点到每个格子的最短距离,并兼作访问标记。初始时,所有格子的距离都设为一个特殊值(如 -1),表示未访问。
- 一个队列,用于存储待访问的节点。队列中的每个元素需要包含节点的状态,即坐标
-
初始化:
- 读取网格、起点和终点坐标。注意将题目中 1-based 的坐标转换为 0-based 的数组索引。
- 将起点
(startX, startY)
的距离dist[startX][startY]
设为 0。 - 将起点的状态
(startX, startY, 0)
加入队列。
-
BFS 循环:
- 当队列不为空时,取出队首元素
(x, y, steps)
。 - 如果
(x, y)
是终点,那么steps
就是最短步数,算法结束。 - 否则,遍历当前格子的四个相邻方向(上、下、左、右),得到新的坐标
(nx, ny)
。 - 对于每个新坐标,进行合法性检查:
- 是否在网格范围内?
- 是否为障碍物?
- 是否已经访问过(即
dist[nx][ny]
不为 -1)?
- 如果新坐标合法且未被访问,则更新其距离
dist[nx][ny] = steps + 1
,并将其状态(nx, ny, steps + 1)
加入队列。
- 当队列不为空时,取出队首元素
-
处理无法到达的情况:
- 如果队列变空后,仍未到达终点,说明从起点无法到达终点。此时,终点的距离值
dist[endX][endY]
仍然是初始值 -1,我们应输出 -1。
- 如果队列变空后,仍未到达终点,说明从起点无法到达终点。此时,终点的距离值
代码
#include <iostream>
#include <vector>
#include <string>
#include <queue>
#include <tuple>
using namespace std;
int main() {
int n, m;
cin >> n >> m;
int sx, sy, ex, ey;
cin >> sx >> sy >> ex >> ey;
// 转换为 0-based 索引
sx--; sy--; ex--; ey--;
vector<string> grid(n);
for (int i = 0; i < n; ++i) {
cin >> grid[i];
}
vector<vector<int>> dist(n, vector<int>(m, -1));
queue<tuple<int, int, int>> q;
// 起点初始化
dist[sx][sy] = 0;
q.push({sx, sy, 0});
// 定义四个方向
int dx[] = {-1, 1, 0, 0};
int dy[] = {0, 0, -1, 1};
while (!q.empty()) {
int x, y, d;
tie(x, y, d) = q.front();
q.pop();
if (x == ex && y == ey) {
cout << d << endl;
return 0;
}
for (int i = 0; i < 4; ++i) {
int nx = x + dx[i];
int ny = y + dy[i];
// 检查新坐标的合法性
if (nx >= 0 && nx < n && ny >= 0 && ny < m &&
grid[nx][ny] == '.' && dist[nx][ny] == -1) {
dist[nx][ny] = d + 1;
q.push({nx, ny, d + 1});
}
}
}
cout << -1 << endl;
return 0;
}
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int m = sc.nextInt();
int sx = sc.nextInt() - 1;
int sy = sc.nextInt() - 1;
int ex = sc.nextInt() - 1;
int ey = sc.nextInt() - 1;
char[][] grid = new char[n][m];
for (int i = 0; i < n; i++) {
grid[i] = sc.next().toCharArray();
}
int[][] dist = new int[n][m];
for (int[] row : dist) {
Arrays.fill(row, -1);
}
Queue<int[]> q = new LinkedList<>();
dist[sx][sy] = 0;
q.offer(new int[]{sx, sy, 0});
int[] dx = {-1, 1, 0, 0};
int[] dy = {0, 0, -1, 1};
while (!q.isEmpty()) {
int[] current = q.poll();
int x = current[0];
int y = current[1];
int d = current[2];
if (x == ex && y == ey) {
System.out.println(d);
return;
}
for (int i = 0; i < 4; i++) {
int nx = x + dx[i];
int ny = y + dy[i];
if (nx >= 0 && nx < n && ny >= 0 && ny < m &&
grid[nx][ny] == '.' && dist[nx][ny] == -1) {
dist[nx][ny] = d + 1;
q.offer(new int[]{nx, ny, d + 1});
}
}
}
System.out.println(-1);
}
}
import sys
from collections import deque
def main():
# 读取输入
n, m = map(int, sys.stdin.readline().split())
sx, sy, ex, ey = map(int, sys.stdin.readline().split())
# 转换为 0-based 索引
sx, sy, ex, ey = sx - 1, sy - 1, ex - 1, ey - 1
grid = [sys.stdin.readline().strip() for _ in range(n)]
# 初始化距离/访问数组
dist = [[-1] * m for _ in range(n)]
# 初始化队列
q = deque()
dist[sx][sy] = 0
q.append((sx, sy, 0))
# 四个方向
dx = [-1, 1, 0, 0]
dy = [0, 0, -1, 1]
while q:
x, y, d = q.popleft()
if x == ex and y == ey:
print(d)
return
for i in range(4):
nx, ny = x + dx[i], y + dy[i]
if 0 <= nx < n and 0 <= ny < m and \
grid[nx][ny] == '.' and dist[nx][ny] == -1:
dist[nx][ny] = d + 1
q.append((nx, ny, d + 1))
print(-1)
if __name__ == "__main__":
main()
算法及复杂度
- 算法:广度优先搜索 (BFS)
- 时间复杂度:每个格子最多被访问一次(入队和出队各一次),对每个格子我们会检查其四个邻居。因此,总时间复杂度为
,其中
和
分别是网格的行数和列数。
- 空间复杂度:空间主要由队列和
dist
数组占用。在最坏的情况下,队列可能需要存储接近所有格子的信息。dist
数组的大小是固定的。因此,总空间复杂度为。