在大屏或电脑上体验更佳——动画和操作区域更宽敞。
输入

A* 寻路

A* 用 f(n) = g(n) + h(n) 排列待搜索节点:g 是已经支付的路径成本,h 是剩余成本的估计。在本课不重新打开已关闭节点的实现中,使用一致启发式可保证最优路径,通常比 Dijkstra 扩展更少的格子。

g 记录实际走过的成本,h 提供目标方向。只看 h 的贪心搜索可能选中昂贵路线;令 h = 0,A* 就变成 Dijkstra。地图、游戏、机器人网格和路线规划常能提供便宜的距离下界,因此适合 A*。

启发式过重预设使用 h = 4 × 曼哈顿距离。它高估余下成本,在便宜绕路尚未探索时就弹出目标,返回非最优路径。这说明启发式条件决定了保证是否成立。

A* 寻路如何工作

算法维护三项状态:g[cell] 保存从起点到此格目前找到的最低成本;came_from[cell] 保存产生该成本的父格;开放集是按 f = g + h 排序的优先队列。

每轮弹出最小 f 的条目。若该格已关闭,丢弃过期条目;若是目标,结束并重建路径;否则关闭该格并检查可通行邻居。计算 tentative_g = g[cur] + move_cost,若低于邻居旧 g,就更新父格、g、f,并把新条目压入开放集。

本实现不重新打开已关闭格,因此需要一致性:每条边满足 h(u) <= cost(u, v) + h(v),且 h(goal) = 0。一致性蕴含可采纳性,即不高估真实剩余成本;只有可采纳但不一致时,算法可能需要重新打开节点,不能套用当前实现的保证。

在一致启发式下,目标第一次被弹出时成本已最优:它具有开放集最小 f,未完成路线的下界不会带来更低的目标成本。本课正常网格中的曼哈顿距离满足这个条件。

A* 寻路逐步演示

默认迷宫有 4 行、7 列,起点为 r1c0,目标为 r1c6。第 3 列的第 0、1、2 行是墙,只能从底部缺口通过,再向上到目标。

下面沿最终路径说明步骤,实际搜索还会扩展其他候选。25 个可通行格中,A* 关闭 15 格,同一迷宫中的 Dijkstra 关闭 23 格。

  1. 初始化 g[r1c0] = 0。曼哈顿距离为 6,所以 f = 0 + 6 = 6。
  2. 弹出并关闭 r1c0。右邻居 r1c1 得到 g=1, h=5, f=6。上下邻居也进入开放集,但偏离目标所在行,所以 f 更大。
  3. 依次弹出 r1c1、r1c2。直线上的格子仍为 f=6,但 r1c2 右侧是墙,无法进入第 3 列。
  4. 路线向下经过 r2c2、r3c2,从 r3c3 穿过缺口,到达 r3c4。每次成功松弛都写入父指针和 g/h/f。
  5. 从 r3c4 经 r2c4、r1c4、r1c5 到达 r1c6。起点后进入十个格子,成本为 10。
  6. 弹出目标时,没有开放条目的 f 更低。依据一致的曼哈顿启发式,算法停止。重建路径为 r1c0 -> r1c1 -> r1c2 -> r2c2 -> r3c2 -> r3c3 -> r3c4 -> r2c4 -> r1c4 -> r1c5 -> r1c6。

切换到启发式过重预设,算法返回顶行直路,成本为 13,而底部绕路只需 6。放大的启发式使算法过早信任目标。最终对比中的红色数字是停止前实际记录的成本,绿色 opt 数字是参考最优路径上的真实累计成本。

A* 与 Dijkstra 的搜索量

默认迷宫中两者路径成本都是 10,但 A* 关闭 15/25 格,Dijkstra 关闭 23/25 格。启发式改变优先级,没有改善最坏渐近复杂度。曼哈顿距离引导搜索靠近目标,g 仍累计绕墙的真实成本;Dijkstra 只按已付成本向外扩展。

时间与空间复杂度

实现时间空间
二叉堆上的网格或图搜索O((V + E) log V)O(V + E)

V 为格子或顶点数,E 为边数。四邻居网格中 E 至多约为 4V,因此常写为 O(V log V)。开放集插入、取最小值和边松弛与 Dijkstra 相同,懒删除堆会保留过期条目,所以空间包含 E。

有便宜且一致的启发式时可用本实现;没有合适估计时用 Dijkstra。若更改为允许重新打开节点的版本,应重新分析对应的条件和搜索成本。

边界情况

  • h=0 有效,等价于 Dijkstra,仍最优但没有目标方向提示。
  • 开放集为空且尚未弹出目标,说明不可达。全部预设都有路径,但代码会为无路情况返回 (None, INF)。
  • f 相同先比较较小 h,再比较行、列,保证回放顺序固定。
  • 加权地形的实际移动成本要加进 g。启发式仍需提供满足条件的剩余成本估计。
  • h 高估时,弹出目标不再证明最优,过重预设就是反例。
  • 绕路预设中,同一个格子先以成本 4 入队,再被改进为 2。旧条目稍后弹出时被跳过,这就是懒删除。

常见错误

  1. 只按 h 排序,变成贪心最佳优先搜索。A* 比较 g + h,更靠近目标也可能因为已付成本太高而落后。
  2. 忽略启发式条件。当前不重新打开节点的代码需要一致性,放大曼哈顿距离会破坏证明。
  3. 入队就关闭格子。弹出前仍可能找到更便宜的路线,应在弹出时关闭。
  4. 忘记过期堆条目。普通二叉堆的懒更新实现会新增条目,并在旧条目弹出时丢弃。
  5. 把画布距离当成本。算法累加网格或图里的移动成本,屏幕布局只是状态的展示。

展示中的简化

开放集画成完全排序的条带,便于观察优先级。真实二叉堆只保证下一次弹出最小条目,其余元素并未完全排序。网格只允许四方向移动,使用固定预设和小整数成本;本课不包含对角移动、动态障碍、路径平滑或游戏专用的平局规则。