这里推荐先去看下B站这个老师讲的BFS迷宫问题,只用看前五分钟就能懂用BFS+队列实现的原理。[POJ] 3984 迷宫问题 BFS_哔哩哔哩_bilibili
问题描述:由m*n的矩阵构成了一个迷宫, 矩阵中为1的元素表示障碍物,不能走,为0表示可以走。同时,可以你可以竖着或横着走都行,也就是四个方向都可以。问:从起点到终点的最短路径是多少,并输出最短路径上的路径信息。
思路:采用广度优先搜索的方式,尝试让某个点朝四个方向各走一步,如果都能走通,将这些路径和步长入队。每次都对队列的元素尝试往前走一步,最早找到终点的路径就是要求的最短路径。我们定义的方向是下、上、右、左。假设由两个点分别向下、向右走一步都到了终点,并且两者的step相等,那么,先遍历的也就是向下的这个路径作为目标输出。即使路径长度都一样,只要一个即可。
广度遍历不需要回溯:开始时用队列存储第一个为遍历的节点。从第一个点开始,将它shift出去,然后尝试向四周走一步,如果走的通,将那个节点和路径信息入队,依次遍历所有方向。
下次遍历时,队列里是步长为1的准备往下一步的节点了。跟之前一样,挨个shift出去,然后你就尝试四周走吧。。。直到已遍历到end节点结束。
大白话思想:从根节点出发,每次只走一步(四个方向都考虑一下),或者阻塞了走不了;走不了也shift出去了,下次不带他继续走了。每次都带着可以走的节点继续往前走。最先走到终点的节点就是循环结束。如果找不到,返回-1。
function shortestPath(maze, start, end) {
const rows = maze.length;
const cols = maze[0].length;
//定义方向
const directions = [
[0, 1], //向下走一步,
[0, -1], //向上走一步
[1, 0], //向右走一步
[-1, 0], //向左走一步
];
const visited = Array.from(Array(rows), () => Array(cols).fill(false));
const queue = [[start, 0, []]];
while (queue.length > 0) {
const [[X, Y], steps, path] = queue.shift();
if (X === end[0] && Y === end[1]) {
return [steps, path.concat([[X, Y]])];
}
for (let dir of directions) {
const [dx, dy] = dir;
const newX = X + dx;
const newY = Y + dy;
if (
newX >= 0 &&
newX < rows &&
newY >= 0 &&
newY < cols &&
maze[newX][newY] == 0 &&
!visited[newX][newY]
) {
visited[newX][newY] = true;
queue.push([[newX, newY], steps + 1, path.concat([[X, Y]])]);
}
}
}
return [-1, []];
}
// 示例迷宫
const maze = [
[0, 1, 0, 0, 0],
[0, 0, 0, 1, 0],
[1, 1, 0, 1, 0],
[1, 1, 0, 0, 0],
[0, 0, 0, 0, 0],
];
const start = [0, 0]; // 起点坐标
const end = [4, 4]; // 终点坐标
const [steps, path] = shortestPath(maze, start, end); // 调用函数计算最短路径
if (steps !== -1) {
console.log(`最短步数为:${steps}`); // 输出最短步数
console.log("最短路径为:" + "[" + path.join("]->[") + "]"); // 输出最短路径
} else {
console.log("未找到路径"); // 输出未找到路径的提示
}
这里用二维数组存储节点已访问的状态。能不能走由四个条件判断:
newX和newY下标必须合理;maze对应位置不能有障碍,当前节点未访问
queue存储的是未遍历四个方向的当前节点。数组中每项存储的是当前遍历的节点座标,走到该节点的步长,节点携带的路径数组。
熟悉js的结构赋值,减少新增变量