在 JavaScript 中实践寻路算法,最常用且易理解的是 A(A-Star)算法 *,它结合了 Dijkstra 算法的准确性和贪心算法的高效性,核心是通过“预估代价”优先探索更可能到达终点的路径。以下是完整实践步骤:
一、核心概念(A* 算法)
A* 的核心是为每个节点计算一个优先级 f = g + h ,优先选择 f 值最小的节点探索:
- g 值:起点到当前节点的实际移动代价(如上下左右移动,每步代价为 1)。
- h 值:当前节点到终点的预估代价(常用“曼哈顿距离”,即 |x1-x2| + |y1-y2| ,仅适用于网格无斜向移动的场景)。
- openList:待探索的节点列表(始终从中选 f 最小的节点)。
- closeList:已探索完成的节点列表(避免重复计算)。
二、完整代码实践(网格寻路)
以“二维网格”为例(0 = 可通行,1 = 障碍物),实现从起点到终点的路径查找,返回路径坐标数组。
javascript:
// 1. 定义节点类:存储坐标、g/h/f 值、父节点(用于回溯路径)
class Node {
constructor(x, y) {
this.x = x; // 横坐标
this.y = y; // 纵坐标
this.g = 0; // 起点到当前节点的实际代价
this.h = 0; // 当前节点到终点的预估代价
this.f = 0; // 总优先级:f = g + h
this.parent = null; // 父节点(记录路径)
}
}
// 2. A* 寻路核心函数
function aStarSearch(grid, startX, startY, endX, endY) {
// 2.1 初始化起点和终点节点
const startNode = new Node(startX, startY);
const endNode = new Node(endX, endY);
// 2.2 初始化 openList(待探索)和 closeList(已探索)
const openList = [startNode];
const closeList = [];
// 2.3 定义移动方向(上下左右,无斜向,每步代价 1)
const dirs = [
{ x: 0, y: -1 }, // 上
{ x: 0, y: 1 }, // 下
{ x: -1, y: 0 }, // 左
{ x: 1, y: 0 } // 右
];
// 3. 循环探索:直到 openList 为空(无路径)或找到终点
while (openList.length > 0) {
// 3.1 从 openList 中选 f 值最小的节点(当前最优节点)
let currentIndex = 0;
let currentNode = openList[0];
for (let i = 1; i < openList.length; i++) {
if (openList[i].f < currentNode.f) {
currentNode = openList[i];
currentIndex = i;
}
}
// 3.2 移除当前节点到 closeList(标记为已探索)
openList.splice(currentIndex, 1);
closeList.push(currentNode);
// 3.3 若找到终点:回溯父节点,生成路径(从终点到起点,最后反转)
if (currentNode.x === endNode.x && currentNode.y === endNode.y) {
let path = [];
let temp = currentNode;
while (temp) {
path.push({ x: temp.x, y: temp.y });
temp = temp.parent;
}
return path.reverse(); // 反转后:起点 → 终点
}
// 3.4 探索当前节点的 4 个相邻节点
for (let dir of dirs) {
// 计算相邻节点坐标
const neighborX = currentNode.x + dir.x;
const neighborY = currentNode.y + dir.y;
// 跳过无效节点:1. 超出网格范围 2. 是障碍物(grid 中为 1) 3. 已在 closeList 中
if (
neighborX < 0 || neighborX >= grid[0].length || // 横向越界
neighborY < 0 || neighborY >= grid.length || // 纵向越界
grid[neighborY][neighborX] === 1 || // 障碍物
closeList.some(node => node.x === neighborX && node.y === neighborY) // 已探索
) {
continue;
}
// 初始化相邻节点(若不在 openList 中,则添加;若已在,则更新 g 值)
let neighborNode = openList.find(node => node.x === neighborX && node.y === neighborY);
if (!neighborNode) {
neighborNode = new Node(neighborX, neighborY);
// 计算 g/h/f 值
neighborNode.g = currentNode.g + 1; // 每步代价 1
neighborNode.h = Math.abs(neighborNode.x - endNode.x) + Math.abs(neighborNode.y - endNode.y); // 曼哈顿距离
neighborNode.f = neighborNode.g + neighborNode.h;
neighborNode.parent = currentNode; // 父节点指向当前节点(记录路径)
openList.push(neighborNode);
} else {
// 若相邻节点已在 openList:检查“经当前节点到达”是否代价更低,是则更新
const newG = currentNode.g + 1;
if (newG < neighborNode.g) {
neighborNode.g = newG;
neighborNode.f = neighborNode.g + neighborNode.h;
neighborNode.parent = currentNode; // 更新父节点(更优路径)
}
}
}
}
// 4. 若 openList 为空,说明无路径
return [];
}
// ------------------- 测试 -------------------
// 定义网格:0 = 可走,1 = 障碍物
const grid = [
[0, 0, 0, 0, 0],
[0, 1, 1, 1, 0],
[0, 0, 0, 0, 0],
[0, 1, 1, 1, 0],
[0, 0, 0, 0, 0]
];
// 起点 (0,0),终点 (4,4)
const path = aStarSearch(grid, 0, 0, 4, 4);
console.log("寻路结果(坐标 x,y):", path);
// 输出示例:[{x:0,y:0}, {x:1,y:0}, {x:2,y:0}, ..., {x:4,y:4}]
三、关键优化与扩展
1. 斜向移动支持:
在 dirs 中添加斜向方向(如 {x:1,y:1} ),并将斜向移动的 g 值设为 Math.sqrt(2) (约 1.414,符合实际距离)。
2. 不同地形代价:
若网格有“草地(代价 1)”“山地(代价 3)”,可将 grid 元素设为代价值(如 2 代表山地),计算 g 值时用 currentNode.g + grid[neighborY][neighborX] 。
3. 性能优化:
- 用“哈希表”或“二维数组”存储 closeList (替代 some 遍历,降低查找时间)。
- 用“优先队列”(如 heap 结构)实现 openList (替代每次遍历找最小 f 值,时间复杂度从 O(n) 降为 O(log n))。
四、应用场景
- 游戏:角色自动寻路(如 RPG 中的 NPC 移动、塔防游戏的敌人路径)。
- 地图:APP 中的步行/驾车路线规划(需结合实际道路数据)。
- 机器人:室内机器人避障导航(网格对应物理空间)。



被折叠的 条评论
为什么被折叠?



