这是一个经典的BFS算法
1. BFS算法保证最短路径
- 核心机制:广度优先搜索按层遍历所有可能的路径,首次到达终点的路径长度即为最短步数。这是BFS的核心优势。
- 队列的作用:通过队列按先进先出的顺序处理节点,确保每一步探索的都是当前层所有可能的节点,不会跳过更短的路径。
2. 坐标与地图的映射
- 地图存储:使用二维数组
map
存储迷宫,其中每个元素的值表示该位置是否可通行(如0
为可通行,1
为障碍物)。 - 坐标处理:你的代码假设输入的起点和终点坐标是 1-based(即从1开始),但在数组中直接以 0-based 索引访问,未做转换。这可能导致越界或定位错误(需修正)。
3. 步数记录与状态管理
- 步数数组
dp
:记录从起点到每个坐标的最小步数。初始化时起点步数为0
(但你的代码中未显式初始化,可能导致逻辑错误)。 - 访问标记:通过
dp[nextx][nexty] == 0
判断节点是否被访问过,避免重复计算。
4. 移动规则与边界检查
- 四个方向移动:通过
dx
和dy
数组定义右、下、左、上四个方向的坐标变化。 - 边界条件:检查坐标是否在
[0, n)
和[0, m)
范围内,防止数组越界。 - 障碍物判断:你的代码中允许移动到
map[nextx][nexty] != 0
的区域,但通常逻辑应为map[nextx][nexty] == 0
表示可通行(此处存在逻辑错误)。
5. 终止条件
- 终点判断:当前节点坐标等于终点坐标时,直接输出步数并返回。
- 无解处理:队列清空后仍未到达终点,输出
-1
表示无解。
代码如下
import java.util.*;
import java.util.concurrent.LinkedBlockingDeque;
// 1:无需package
// 2: 类名必须Main, 不可修改
public class Main {
public static class node {
int x;
int y;
public node(int x, int y) {
this.x = x;
this.y = y;
}
}
public static void main(String[] args) {
// 创造点对象,方便数据操作
// 创建一个数组用来记录地图,地图为nxm,
// 我们将之后的地图边界设置为1,防止索引越界,地图边界为1-n,1-m,
// 创建一个数组记录该位置累计的最小的步数
// 创建一个需要拓展点的队列
// 这个队列是很有必要的,若是这个队列里面有值,就代表可以拓展,就可以避免走不出去的情况
// 开始向四个方向的路走,要判断这个方向的路是否可走(边界、障碍物)、
// 如果可走就将该点存入队列,确保队列还有值,还能继续在迷宫里走
// 终止条件,是否到达终点,到终点就将值传进去
//基础传值操作
Scanner scan = new Scanner(System.in);
int n = scan.nextInt();
int m = scan.nextInt();
int[][] map = new int[n][m];
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
map[i][j] = scan.nextInt();
}
}
int startx = scan.nextInt();
int starty = scan.nextInt();
int endx = scan.nextInt();
int endy = scan.nextInt();
//这里有一个混淆点,题目给我们的坐标(1,1) (5,5)但对应在数组上的索引应该是[0,0] [4,4]
node start = new node(startx-1, starty-1);
node end = new node(endx-1, endy-1);
//只需要一张地图和起点,终点
BFS(map, start, end);
scan.close();
}
public static void BFS(int[][] map, node start, node end) {
//数据初始化
int n = map.length;
int m = map[0].length;
int[] dx = {0, 1, 0, -1};
int[] dy = {1, 0, -1, 0};
int[][] dp = new int[n][m];
// Arrays.stream(dp).forEach(ints -> Arrays.fill(ints, -1));
// if (map[start.x][start.y] == 1) {
// System.out.println(-1);
// return;
// }
//初始化队列将起点放进去0
Queue<node> queue = new LinkedBlockingDeque<>();
queue.add(start);
//只要队列不为空就可以一直走
while (!queue.isEmpty()) {
node now = queue.poll();//从队列里面取值,取完值就得将该值删除
//当前节点等于终点直接输出结果
if (now.x == end.x && now.y == end.y) {
System.out.println(dp[now.x][now.y]);
return;
}
int nowx = now.x;
int nowy = now.y;
for (int i = 0; i < 4; i++) {
int nextx = nowx + dx[i];
int nexty = nowy + dy[i];
node next = new node(nextx, nexty);
//边界判断 x[0,n) y[0,m) ,还有地图不能等于1,并且这个地方没被走过
if (
(nextx >= 0 && nextx < n) &&
(nexty >= 0 && nexty < m) &&
(map[nextx][nexty] != 0) &&
(dp[nextx][nexty] == 0)
) {
queue.add(next);//下一步可走就将该点加入队列,假如若有四次可走就将这四个点都加入队列,
//之后就可以从这队列里面取可走的点然后继续拓展可走的点的四个方向
dp[nextx][nexty] = dp[nowx][nowy] + 1;//走过的地方步数都大于1
}
}
}
//无法到达终点
System.out.println(-1);
}
}
这里有一个混淆点,由于他题目给的坐标和索引不对应导致卡了半天没想到居然还能过71.4%