C语言经典算法之A*搜索算法

目录

前言

A.建议

B.简介

一 代码实现

二 时空复杂度

A.时间复杂度(Time Complexity):

B.空间复杂度(Space Complexity):

C.总结

三 优缺点

A.优点:

B.缺点:

四 现实中的应用


前言

A.建议

1.学习算法最重要的是理解算法的每一步,而不是记住算法。

2.建议读者学习算法的时候,自己手动一步一步地运行算法。

B.简介

A*搜索算法(A-Star Search Algorithm)是一种启发式搜索算法,主要用于在图形或网络结构中寻找从起始节点到目标节点的最短路径。它结合了Dijkstra算法确定性的最优性(保证找到的是实际最短路径)和Best-First Search(最佳优先搜索)的高效性(通过启发式信息指导搜索方向),在保证找到最优解的同时尽可能减少搜索范围。

一 代码实现

数据结构定义: 首先,我们需要定义一些数据结构来表示搜索空间中的节点。每个节点通常包含位置信息、从起始点到该节点的代价 g(n)、估计到达目标点的成本 h(n) 以及父节点引用。

typedef struct Node {
    int x, y; // 假设这是一个二维地图上的节点坐标
    double g; // 从起始节点到当前节点的实际代价
    double h; // 到达目标节点的启发式估计代价
    double f; // f(n) = g(n) + h(n),综合评估函数值
    struct Node* parent; // 指向父节点的指针,用于追踪路径
} Node;

// 可能还会有用于存储整个地图或开放列表和关闭列表的数据结构

初始化: 初始化起始节点,设置其 g 值为0,计算 h 值,并将起始节点添加到开放列表中。

Node start;
start.x = startX;
start.y = startY;
start.g = 0;
start.h = heuristic(start, target); // 使用某种启发式函数计算初始h值
start.f = start.g + start.h;
pushToOpenList(&start);

启发式函数: 定义一个启发式函数,它提供了一种对从当前节点到目标节点距离的估算。例如,在网格环境中,可以使用曼哈顿距离或欧几里得距离作为启发式函数。

double heuristic(Node current, Node goal) {
    return abs(current.x - goal.x) + abs(current.y - goal.y); // 曼哈顿距离
    // 或者
    return sqrt(pow((current.x - goal.x), 2) + pow((current.y - goal.y), 2)); // 欧几里得距离
}

开放列表与关闭列表管理: 创建两个集合,一个是开放列表(Open List),存放待检查的节点;另一个是关闭列表(Closed List),存放已检查过的节点。

搜索循环: 在每一步迭代中,从开放列表中选择具有最小 f 值的节点(最优节点)。如果这个节点是目标节点,则搜索结束,开始回溯构建最终路径。否则,将当前节点移到关闭列表,并检查其所有邻居节点:

while (!isEmpty(openList)) {
    Node* currentNode = findMinFInOpenList(openList); // 获取当前最优节点
    if (isSameNode(currentNode, &target)) { // 达到目标节点
        reconstructPath(currentNode); // 回溯路径
        break;
    }
    
    removeFromOpenList(openList, currentNode);
    addToClosedList(closedList, currentNode);

    for (each neighbor in currentNode's neighbors) {
        if (neighbor is not in closedList) {
            double tentative_g = currentNode->g + getCost(currentNode, neighbor);
            if (tentative_g < neighbor->g || neighbor not in openList) {
                neighbor->parent = currentNode;
                neighbor->g = tentative_g;
                neighbor->h = heuristic(neighbor, target);
                neighbor->f = neighbor->g + neighbor->h;
                if (neighbor not in openList) {
                    pushToOpenList(neighbor);
                }
            }
        }
    }
}

// 清理并输出结果

路径回溯: 从目标节点开始,通过父节点引用一路回溯到起始节点,从而得到从起始到目标的最短路径。

void reconstructPath(Node* node) {
    while (node != NULL) {
        // 将节点添加到路径列表或打印节点坐标
        node = node->parent;
    }
}

请注意以上代码仅为概念性的伪代码片段,实际编写时需要根据具体问题填充缺失的细节,比如如何实现集合操作(如插入、删除、查找等)、如何处理连续空间中的邻接关系等。同时,为了提高效率,可能还需要引入优先队列来优化开放列表的操作,以保证每次都能直接获取当前最低f值的节点。

二 时空复杂度

A.时间复杂度(Time Complexity):

  • 最好情况:如果启发式函数h(n)是完美的,即对于每个节点,h(n)的值恰好等于从该节点到目标节点的实际最小代价,那么A*搜索将会模拟Dijkstra算法的行为,并且在找到解时达到最优。在这种情况下,最坏情况下的时间复杂度是 O(|E| + |V| log_2|V|),其中|E| 表示图中边的数量,|V|表示顶点数量。这是因为通常使用优先队列(如二叉堆)来保持开放列表,每次插入、删除和查找操作的时间复杂度为O(log_2|V|)

  • 最坏情况:当启发式函数不是理想的低估函数时,A可能需要检查更多的节点才能找到解。此时时间复杂度难以精确估计,因为它依赖于启发式函数的质量以及图的具体结构。实际运行时可能会接近 O(b^d),其中 b 是平均 branching factor(每个节点的邻居数),d 是实际路径长度。在具有大量节点和边的图上,A的时间效率会显著降低,尤其是当启发式函数给出较差指引时。

B.空间复杂度(Space Complexity):

  • A*搜索的空间复杂度主要体现在存储开放列表、关闭列表和路径信息等方面。
  • 在最坏情况下,理论上空间复杂度可以达到O(|V|),即需要存储所有顶点的状态信息,特别是当图是高度连通或者搜索过程需要遍历大部分节点时。
  • 实际应用中,为了优化内存占用,可以通过限制开放列表的大小或采用其他策略减少所需存储空间。

C.总结

总之,A*搜索算法的时间复杂度与启发式函数选择、图的结构和规模等因素密切相关,而空间复杂度则主要取决于需要记录的节点状态数量。良好的启发式函数能够有效引导搜索,从而在一定程度上降低时空复杂度。

三 优缺点

A.优点:

  1. 最优性保证

    • 当启发式函数 h(n) 是一致的(即对于任意节点n和其任何后继节点m,从起始节点到m的路径经过n时,h(n) <= h(m)),并且是可接纳的(即它永远不会高估实际代价),A*可以确保找到从起始节点到目标节点的最短路径。
  2. 效率高

    • 通过结合了实际成本 g(n) 和启发式估价 h(n) 的评估函数 f(n),A*能够优先考虑最有希望到达目标的节点,有效地减少了搜索空间。
    • 对于某些问题,尤其是当启发式函数能提供很好的近似值时,A*可以在较短时间内找到解决方案。
  3. 适应性强

    • A*适用于各种类型的问题,包括但不限于路径规划、游戏AI寻路、机器人导航等,只要能定义出合适的距离度量和启发式函数即可。
  4. 灵活性

    • 可以根据问题的具体特性定制启发式函数,使其在不同的应用中表现出良好的性能。

B.缺点:

  1. 计算资源需求

    • 如果图非常大或者高度连通,即使使用了一致且可接纳的启发式函数,A*可能需要处理大量的节点,导致内存占用增加和计算时间延长。
  2. 启发式函数的质量

    • 启发式函数的选择对算法性能至关重要。如果启发式函数设计不佳,可能会导致搜索效率低下甚至找不到解。特别是在复杂环境中,设计一个既准确又高效的启发式函数具有一定的挑战性。
  3. 存储开销

    • 为了维护开放列表(待访问节点)和关闭列表(已访问节点),A*需要额外的存储空间,尤其在处理大规模数据集时,这会成为限制因素。
  4. 无法应对动态变化环境

    • 在实时动态变化的环境中,一旦环境发生变化,之前计算出的成本信息可能失效,需要重新计算或采用其他策略。
  5. 局部最优陷阱

    • 在某些情况下,虽然A*总体上能够避免陷入局部最优解,但较差的启发式函数可能导致算法长时间集中在某个局部区域,尤其是在路径复杂度较高的场景下。

四 现实中的应用

  1. 游戏开发

    • 在实时策略(RTS)游戏中,用于寻路算法来计算单位从一个位置移动到另一个位置的最优路径,避免障碍物。
    • 在冒险类游戏中,帮助游戏角色自动导航通过迷宫、地形或其他复杂环境。
  2. 机器人导航

    • 无人驾驶车辆和无人机中使用A*搜索算法规划出安全且距离较短的行驶路径,避开障碍并到达目标点。
    • 家用服务机器人如扫地机器人利用A*算法找到最有效率的清洁路线。
  3. 地图应用

    • 导航软件如Google Maps、Apple Maps等在计算两点间的最佳路径时,可以采用A*算法处理复杂的道路网络,考虑交通状况、转弯限制等因素。
  4. 物流与配送优化

    • 快递公司和物流公司规划配送员的最佳送货路径,以减少运输成本和时间消耗。
  5. 图形用户界面(GUI)设计工具

    • 设计工具中的布局引擎可能会用A*算法自动排列组件,使得它们之间的连接线尽可能简洁。
  6. 计算机视觉与图像处理

    • 在图像分割、对象追踪等任务中,有时会结合A*搜索来确定像素间最优路径或者在图像中找寻特定形状的最短路径。
  7. 人工智能和规划问题

    • 在更广泛的AI和规划领域,A*被用来解决各种形式的状态空间搜索问题,比如棋类游戏的树搜索,或者是机械臂动作序列规划等。
  8. GIS地理信息系统

    • 地理信息系统中进行地形分析和路径规划,例如在山区寻找登山路径或规划城市基础设施建设的最优线路。

你可能感兴趣的:(C语言经典算法,算法,c语言,开发语言,数据结构)