A*寻路算法(一):路径搜索算法演进:Dijkstra、最佳优先搜索与 A*

253 阅读12分钟

为了深入理解 A* 算法的精妙之处及其解决的问题,我们有必要先了解它的前身:Dijkstra 算法和最佳优先搜索(Best-First Search)。通过对比它们的核心思想、优势与局限,能更清晰地认识到 A* 算法如何融合二者的优点,最终成为路径规划领域的基石算法。

一、 Dijkstra 算法:稳健但昂贵的全局探索者

Dijkstra 算法是一种经典的单源最短路径算法,由荷兰计算机科学家 Edsger W. Dijkstra 于 1956 年提出。它用于在边权重非负有向图或无向图中,计算从一个起始节点图中所有其他节点的最短路径。

核心思想:贪心策略

算法采用贪心策略,逐步确定起点到各顶点的最短路径:

  1. 初始化:起点到自身距离设为 0,到其他所有点距离设为无穷大 (∞)。
  2. 选择:每次从未处理的顶点集合中,选择距离起点最近的顶点 u
  3. 松弛(Relaxation) :考察 u 的所有邻居顶点 v。计算 新距离 = u 的距离 + u 到 v 的边权重。若此新距离小于 v 的当前距离,则更新 v 的距离值。
  4. 标记:将 u 标记为“已处理”。
  5. 重复:重复步骤 2-4,直到所有顶点都被处理。

算法流程

  1. 初始化

    • 创建距离表 (dist): dist[start] = 0dist[v] = ∞ (v ≠ start)。
    • 创建已处理集合 (visited 或 closedSet): 记录已确定最短路径的顶点 (初始为空)。
    • 创建优先队列(最小堆)  (openSet): 按 dist 值排序,将起点加入队列 (dist[start] = 0)。
  2. 主循环 (当 openSet 不为空):

    • 取出:从 openSet 中取出 dist 值最小的顶点 u

    • 标记:将 u 加入 visited

    • 遍历邻居:对于 u 的每个邻居 v

      • 如果 v 已在 visited 中,跳过。

      • 计算新距离 newDist = dist[u] + weight(u, v)

      • 如果 newDist < dist[v]

        • 更新 dist[v] = newDist
        • 记录 v 的前驱节点为 u (用于回溯路径)。
        • 将 v 加入 openSet (如果尚未在队列中,或更新其在队列中的优先级)。
  3. 终止

    • 当 openSet 为空时,算法结束。
    • 此时 dist 表中存储的就是起点到所有顶点的最短距离,结合前驱节点记录可回溯出具体路径。

优势与局限

  • 优势

    1. 保证最优解:在非负权重图中,总能找到起点到所有点的绝对最短路径
    2. 全局信息:一次性计算出起点到所有其他节点的最短路径。
  • 局限性

    1. 内存消耗高:需要维护完整的全局距离表 (dist) 和节点状态。
    2. 计算冗余:采用“均匀扩散”策略,会探索大量与特定目标无关的节点。当只需起点到单一终点的路径时,效率低下。
    3. 时间复杂度:通常为 O((V + E) log V) (使用优先队列),在大规模图上可能较慢。不适用于边权重为负的图。

二、 最佳优先搜索算法 (Best-First Search):敏捷但可能迷失的方向追寻者

最佳优先搜索是一种基于启发式评估的路径搜索算法。其核心思想是利用一个启发式函数 h(n) 预估当前节点 n 到目标节点的代价,并始终优先扩展预估代价最小的节点(即看起来最有希望接近目标的节点)。它不保证找到最短路径,但能快速找到一个可行解。

核心思想:启发式引导

算法完全依赖启发式函数 h(n) 引导搜索方向:

  1. 仅使用 h(n) 评估节点 n 到目标的预估代价
  2. 始终优先扩展当前**h(n) 值最小**的节点(最有希望节点)。
  3. 不考虑从起点到当前节点的实际累积代价 (g(n))。

算法流程

  1. 初始化

    • 创建优先队列(最小堆)  (openSet): 按启发值 h(n) 排序。
    • 创建已访问集合 (visited): 防止重复处理节点 (初始为空)。
    • 将起点加入 openSet,记录其启发值 h(start)
  2. 主循环 (当 openSet 不为空):

    • 取出:从 openSet 中取出 h(n) 值最小的顶点 u

    • 标记:将 u 加入 visited

    • 检查目标:如果 u 是目标节点,终止循环并回溯路径 (需记录父节点)。

    • 遍历邻居:对于 u 的每个邻居 v

      • 如果 v 在 visited 中或是障碍物,跳过。
      • 计算 v 的启发值 h(v)
      • 记录 v 的父节点为 u (用于回溯路径)。
      • 将 v 加入 openSet (如果未访问过)。
  3. 终止条件

    • 找到目标节点:成功,返回路径。
    • openSet 为空:失败,无路径可达。

优势与局限

  • 优势

    1. 搜索速度快:启发式函数引导搜索直奔目标方向,避免探索无关区域。
    2. 内存消耗相对较低:不需要维护全局路径的精确代价 (g(n))。
    3. 实现简单:核心只需一个启发式函数 h(n)
  • 局限性

    1. 不保证最优解:找到的路径可能不是最短的。如果启发式函数 h(n) 高估了实际代价,可能错过最优路径;如果 h(n) 低估,则可能效率降低。
    2. 不完备性:差的启发式函数可能导致算法在环路中打转或找不到解(即使存在)。
    3. 对启发式依赖性强:性能和解的质量高度依赖于 h(n) 的设计质量。

三、 A * 算法:智慧平衡的寻路大师

A* (A-Star) 算法诞生于 1968 年,由斯坦福研究院 (SRI) 的 Peter Hart, Nils Nilsson 和 Bertram Raphael 在著名的 Shakey 机器人项目中首次正式提出(论文《A Formal Basis for the Heuristic Determination of Minimum Cost Paths》)。它巧妙融合了 Dijkstra 的严谨最优性保证和最佳优先搜索的方向性效率,成为路径规划领域的里程碑。

诞生背景

  1. Shakey 机器人项目:A* 为世界上第一个通用移动机器人 Shakey 开发,用于在包含静态障碍物的二维网格世界中自主规划起点到目标点的路径。当时的计算机资源极其有限,亟需高效且可靠的算法。

  2. 已有算法的局限

    算法主要问题
    Dijkstra计算所有节点最短路径,效率低下,内存消耗大。
    广度优先搜索类似 Dijkstra,资源消耗大。
    最佳优先搜索仅依赖启发式 (h(n)),无法保证找到最短路径,可能陷入非最优解或死循环。

A* 旨在解决

  1. 效率问题:减少像 Dijkstra 那样不必要的全局探索。
  2. 最优性保证:确保像 Dijkstra 那样找到最短路径,克服最佳优先搜索的缺陷。
  3. 资源可行性:在 Shakey 有限的硬件条件下实际可用。

核心思想:融合实际代价与启发预估

A* 是一种启发式搜索算法。它为每个节点 n 定义了一个关键的评价函数:
F(n) = G(n) + H(n)

  • G(n) :从起点到当前节点 n 的实际移动代价 (相当于 Dijkstra 中的 dist[n])。
  • H(n) :从当前节点 n 到目标节点预估代价 (启发式函数,Heuristic)。
  • F(n) :节点 n 的总评估值,用于决定节点的扩展优先级(F(n) 最小的节点优先扩展)。

算法核心

  • G(n) 保证了路径的实际最优性基础
  • H(n) 提供了目标导向性,引导搜索朝向最有希望的方向。
  • 优先扩展 F(n) 最小的节点,意味着算法在探索时,既考虑已经付出的代价 (G(n)),又考虑预计还需的代价 (H(n)),在最优性和效率之间取得智能平衡。算法的平均性能高度依赖于 H(n) 的质量。

算法步骤

  1. 初始化

    • 开放列表 (openSet) :存储待考察节点(优先队列/最小堆,按 F(n) 排序)。将起点加入,计算其 G(start)=0H(start)F(start)=G+H
    • 关闭列表 (closedSet) :存储已考察且最短路径已确定的节点(集合)。初始为空。
    • 记录父节点信息:用于回溯最终路径。
  2. 寻路主循环 (当 openSet 不为空):

    1. 取出节点:从 openSet 中取出 F(n) 值最小的节点 current

    2. 目标检查:若 current 是目标节点,则终止循环,回溯路径

    3. 移入关闭列表:将 current 加入 closedSet

    4. 遍历邻居:对 current 的每个有效邻居 neighbor

      • 跳过条件:若 neighbor 是障碍物或在 closedSet 中,跳过。

      • 计算临时 G 值tentative_g = G(current) + cost(current, neighbor)

      • 首次访问或发现更优路径:若 neighbor 不在 openSet 中,或 tentative_g < G(neighbor) (说明找到一条到 neighbor 的更短路径):

        • 记录/更新父节点neighbor 的父节点设为 current
        • 更新 G, H, F: G(neighbor) = tentative_g;计算 H(neighbor)F(neighbor) = G(neighbor) + H(neighbor)
        • 加入/更新开放列表:若 neighbor 不在 openSet,加入;若已在,则更新其在优先队列中的 F 值(可能需要调整堆结构)。
  3. 终止条件

    • 找到目标:成功,通过父节点回溯路径。
    • 开放列表为空:失败,起点与终点之间无有效路径。
示例:

image.png

关键特性与要求

  • 可采纳性 (Admissibility) :如果启发函数 H(n) 永远不会高估从节点 n 到目标的实际最小代价(即 H(n) <= 实际最小代价),则 A* 保证找到最短路径。常见可采纳启发式如曼哈顿距离(仅允许四方向移动的网格)、欧几里得距离(允许任意方向移动)。
  • 一致性/单调性 (Consistency/Monotonicity) :如果对于任意节点 n 及其后继节点 n',满足 H(n) <= cost(n, n') + H(n'),则称 H(n) 是一致的。一致的启发式必然是可采纳的,且能保证当节点第一次被取出 openSet 时,其 G(n) 值就是最优的,无需重新打开检查(简化实现)。欧几里得距离通常是一致的。

综合对比:Dijkstra vs. 最佳优先搜索 vs. A*

特性A*Dijkstra最佳优先搜索 (贪心)
评估标准F(n) = G(n) + H(n)  (实际+启发)仅 G(n)  (实际累积代价)仅 H(n)  (启发式预估代价)
解质量保证最优 (当 H(n) 可采纳时)保证最优可能非最优
搜索效率 (好的 H(n) 引导下) (探索范围广)非常高 (直奔目标,但可能绕路)
内存使用中等 (需存 G, H, F, 状态) (需存所有节点距离和状态) (主要依赖 H(n) 和状态)
完备性 (在有限图中有解则能找到) (可能因差 H(n) 陷入死循环)
依赖启发式 (性能和解质量依赖 H(n) 质量) (行为完全由 H(n) 驱动)
典型适用场景游戏 AI, 机器人导航, GPS 路径, 网格寻路网络路由, 全局路径规划, 无权图搜索快速估算路径, 实时性要求高 (接受次优)

优缺点与应用场景总结

算法优点缺点典型适用场景
Dijkstra✅ 严格保证最短路径 ✅ 适用于任意非负权重图❌ 速度最慢 (全局均匀探索) ❌ 内存消耗最高 (存储全局信息)网络路由协议、需全局所有节点最短路径、无权图搜索
最佳优先搜索✅ 速度通常最快 (目标导向性强) ✅ 内存占用最低❌ 不保证最优解 ❌ 不完备 (可能迷路/死循环) ❌ 高度依赖启发式质量快速获得可行路径 (不要求最优)、实时决策、启发式信息强且可靠的场景
A*✅ 保证最短路径 (H(n) 可采纳时) ✅ 效率显著高于 Dijkstra (好的 H(n) 引导下) ✅ 平衡了效率与最优性❌ 启发式 H(n) 设计需技巧 (需可采纳/一致) ❌ 内存高于最佳优先搜索 (需存 G(n)) ❌ 速度可能低于最佳优先搜索 (需计算 G(n))游戏角色寻路、机器人路径规划、GPS导航、网格地图寻路 (需最优或接近最优路径)

形象比喻助记

  1. Dijkstra 算法 ------ 严谨的扫地机器人

    • 特点:  像一滴均匀扩散的墨水渍,不紧不慢地向所有方向探索。
    • 行为:  每一步都确保找到当前已知的最短路径,绝不冒险。最终一定能找到最优解,但可能因为它完全不知道目标在哪里而探索大量无关区域,效率较低。
  2. 最佳优先搜索 (贪心) ------ 鲁莽的猎犬

    • 特点:  像一只闻到猎物气味的猎犬,疯狂地朝它认为目标所在的方向冲刺。
    • 行为:  速度极快,完全依赖直觉 (H(n) 气味)。但它不考虑已经跑了多远 (G(n)),可能冲进死胡同或因为被错误的气味 (H(n) 不准确) 误导而绕远路。不一定能找到最短路径,甚至可能完全迷失方向
  3. A* 算法 ------ 聪明的导航专家

    • 特点:  结合了扫地机器人的严谨猎犬的方向感,像一个手持精确里程表 (G(n))  和可靠指南针/地图 (H(n))  的探险家。
    • 行为:  每一步既计算实际已经走过的精确距离 (G(n)) ,又科学预估剩余路程 (H(n)) ,选择 总预估代价 (F(n)) 最小的路线前进 (F(n)=G(n)+H(n))。在地图和指南针可靠 (H(n) 可采纳)  的前提下,保证找到最短路径,同时比扫地机器人 (Dijkstra) 快得多。