A*算法实现原理以及实现步骤(C++)
算法原理:
A*算法是一种启发式搜索算法,用于在图中寻找最短路径。它结合了Dijkstra算法的确保最短路径的优点和贪心最佳优先搜索的高效性。其核心在于使用一个评估函数:
f(n) = g(n) + h(n)
其中:
- g(n) 表示从起点到节点n的实际代价。
- h(n) 表示从节点n到目标节点的估计代价(启发式函数)。
1. 启发式函数h(n):必须满足可采纳性(即永远不会高估实际代价)以保证找到最短路径。常见的启发式函数有曼哈顿距离(适用于网格中只能上下左右移动)和欧几里得距离。
2. 开放列表(Open List):存储待考察的节点,通常用优先队列(小顶堆)实现,按f值排序。
3. 封闭列表(Closed List):存储已考察过的节点,避免重复处理。
4. 节点数据结构:通常包含:
- 位置信息(坐标)
- g值(从起点到该节点的实际代价)
- h值(启发式估计值)
- f值(f = g + h)
- 父节点指针(用于回溯路径)
A* 算法设计原理
A* 算法是一种启发式搜索算法,用于在图中寻找从起点到目标点的最优路径。它结合了 Dijkstra 算法的完备性(保证找到最短路径)和贪心最佳优先搜索的高效性。
核心公式:
f(n) = g(n) + h(n)
g(n)
:从起点到节点n
的实际代价
h(n)
:从节点n
到目标的估计代价(启发式函数)
f(n)
:节点n
的综合优先级关键要求:
可采纳性:
h(n)
必须 ≤ 实际最小代价(不能高估)一致性:
h(n) ≤ c(n, n') + h(n')
(三角不等式)数据结构:
开放列表 (Open List):优先队列,存储待探索节点(按
f(n)
排序)封闭列表 (Closed List):记录已探索节点
节点信息:坐标、
g
值、h
值、父节点指针
算法步骤:
- 初始化:将起点加入开放列表。
- 循环直到找到目标或开放列表为空:
1. 从开放列表中取出f值最小的节点(作为当前节点)。
2. 如果该节点是目标节点,则回溯构建路径并返回。
3. 将该节点移入封闭列表。
4. 遍历当前节点的所有邻居:
- 如果邻居在封闭列表中,则跳过。
- 如果邻居不在开放列表中,则计算其g、h、f值,设置父节点为当前节点,并加入开放列表。
- 如果邻居已在开放列表中,则检查通过当前节点到达该邻居是否具有更小的g值(即更优路径),如果是,则更新其g值和f值,并重新调整优先队列。
该部分可参见---> A*
此时,I节点总代价最小,会以I节点继续作为父节点,继续搜索邻近节点。
C++实现A*算法具体流程:
具体实现步骤(C++)
我们将实现一个在二维网格上运行的A*算法。网格中0表示可通行,1表示障碍物。
数据结构
1. 节点(Node):包含坐标(x,y),g值,h值,f值,父节点坐标。
2. 比较函数:用于优先队列,按f值从小到大排列(若f值相同,可以按h值排序)。
步骤
1. 创建两个列表:开放列表(优先队列)和封闭列表(可以用二维数组或哈希集合)。
2. 初始化起点的g=0,h=启发式函数计算,f=g+h,并将起点加入开放列表。
3. 循环直到开放列表为空:
- 取出f值最小的节点作为当前节点。
- 若当前节点为目标节点,回溯路径并返回。
- 将当前节点标记为已访问(加入封闭列表)。
- 遍历当前节点的八个邻居(或四个,这里我们实现八个方向的移动):
- 跳过障碍物和不可达位置。
- 如果邻居不在封闭列表中:
- 计算临时g值(当前节点的g + 移动到邻居的代价,直线为1,斜线为√2,但为了简单,我们假设直线代价10,斜线代价14,这样避免浮点运算)。
- 如果该邻居不在开放列表中,则加入。
- 如果在开放列表中,但新的g值更小,则更新其g值和f值,并调整优先队列(由于标准优先队列不支持更新,我们采用懒惰删除:即当节点被取出时检查是否已被访问,同时允许重复加入节点,但通过封闭列表避免重复处理。或者使用可更新的优先队列,但C++标准库不直接支持,所以我们采用允许重复加入但用封闭列表过滤,并在取出时判断是否已经在封闭列表中。另外,我们也可以在节点进入开放列表时记录状态,当有更优路径时,我们直接更新节点的值,然后重新调整优先队列。然而,由于标准优先队列不支持更新,我们可以使用multiset或者set来手动实现更新,或者使用一个二维数组来记录每个节点的最佳g值,这样当遇到重复节点时,如果新的g值更小,我们就重新加入一个节点(即同一个位置有两个节点,但g值不同),然后旧的节点在取出时会被封闭列表过滤掉。这里我们采用后一种方法:用一个二维数组`gScore`记录每个节点的最佳g值,这样在遇到同一个节点时,如果新的g值更小,我们就重新生成一个节点加入开放列表,而旧节点即使还在队列中,当它被取出时,我们会检查它的g值是否与当前记录的最佳g值一致,如果不一致则跳过(或者用封闭列表,但封闭列表是在节点被处理后才加入,所以我们可以不检查,因为新节点g值更小,会先被处理)。然而,更简单的方法是:每当计算邻居时,如果新的g值小于该邻居之前记录的g值,那么我们就更新这个邻居的g值,并重新加入开放列表(尽管它已经在队列中,但我们允许重复,因为新的f值更小,所以会先被取出)。然后当从开放列表中取出节点时,我们检查它是否已经被处理过(即封闭列表),如果已经处理过则跳过。
优化点
- 使用一个二维数组gScore来存储每个位置的最佳g值,初始为无穷大。
- 当计算临时g值(tentative_g)小于该邻居的gScore时,更新gScore,并将该节点(新的g值)加入开放列表。
#include <iostream> #include <vector> #include <queue> #include <cmath> #include <algorithm> #include <set>using namespace std;// 定义节点结构 struct Node {int x, y; // 节点坐标double g, h, f; // 代价值Node* parent; // 父节点指针Node(int x, int y, Node* parent = nullptr) : x(x), y(y), g(0), h(0), f(0), parent(parent) {}// 重载比较运算符(用于优先队列)bool operator<(const Node& other) const {return f > other.f; // 注意:优先队列默认最大堆,反向定义实现最小堆} };// 比较函数对象(用于优先队列) struct NodeCompare {bool operator()(const Node* a, const Node* b) const {return a->f > b->f; // f值小的优先级高} };// 移动方向定义(8个方向) const int dx[8] = {1, 0, -1, 0, 1, 1, -1, -1}; const int dy[8] = {0, 1, 0, -1, 1, -1, 1, -1}; const double costStraight = 1.0; // 直线移动代价 const double costDiagonal = 1.414;// 对角线移动代价(√2)// 启发式函数(欧几里得距离) double heuristic(int x1, int y1, int x2, int y2) {return sqrt(pow(x1 - x2, 2) + pow(y1 - y2, 2)); }// A* 算法核心实现 vector<pair<int, int>> aStarSearch(vector<vector<int>>& grid, pair<int, int> start, pair<int, int> goal) {int rows = grid.size();int cols = grid[0].size();// 开放列表(优先队列)priority_queue<Node*, vector<Node*>, NodeCompare> openList;// 封闭列表(二维数组标记)vector<vector<bool>> closedList(rows, vector<bool>(cols, false));// 起点节点初始化Node* startNode = new Node(start.first, start.second);startNode->h = heuristic(start.first, start.second, goal.first, goal.second);startNode->f = startNode->g + startNode->h;openList.push(startNode);while (!openList.empty()) {// 获取f值最小的节点Node* current = openList.top();openList.pop();int x = current->x;int y = current->y;// 到达目标点:回溯生成路径if (x == goal.first && y == goal.second) {vector<pair<int, int>> path;while (current) {path.push_back({current->x, current->y});current = current->parent;}reverse(path.begin(), path.end());return path;}// 当前节点加入封闭列表closedList[x][y] = true;// 遍历所有邻居方向for (int i = 0; i < 8; ++i) {int nx = x + dx[i];int ny = y + dy[i];// 检查邻居是否合法if (nx < 0 || nx >= rows || ny < 0 || ny >= cols || grid[nx][ny] == 1 || closedList[nx][ny]) {continue;}// 计算移动代价(判断直线/对角线)double moveCost = (abs(dx[i]) + abs(dy[i]) == 2) ? costDiagonal : costStraight;double newG = current->g + moveCost;// 创建邻居节点Node* neighbor = new Node(nx, ny, current);neighbor->g = newG;neighbor->h = heuristic(nx, ny, goal.first, goal.second);neighbor->f = neighbor->g + neighbor->h;// 加入开放列表openList.push(neighbor);}}return {}; // 未找到路径 }// 打印网格和路径 void printSolution(const vector<vector<int>>& grid, const vector<pair<int, int>>& path) {vector<vector<char>> display = vector<vector<char>>(grid.size(), vector<char>(grid[0].size(), ' '));// 标记障碍物for (int i = 0; i < grid.size(); ++i) {for (int j = 0; j < grid[0].size(); ++j) {if (grid[i][j] == 1) display[i][j] = '#';}}// 标记路径(起点和终点特殊标记)for (const auto& p : path) {if (p == path.front()) display[p.first][p.second] = 'S';else if (p == path.back()) display[p.first][p.second] = 'G';else display[p.first][p.second] = '*';}// 打印结果for (const auto& row : display) {for (char c : row) {cout << '[' << c << ']';}cout << endl;} }int main() {// 示例网格:0=空地, 1=障碍物vector<vector<int>> grid = {{0, 0, 0, 0, 0, 0},{0, 1, 1, 0, 1, 0},{0, 0, 0, 0, 1, 0},{0, 1, 1, 1, 1, 0},{0, 0, 0, 0, 0, 0}};pair<int, int> start = {0, 0};pair<int, int> goal = {4, 5};// 执行A*搜索vector<pair<int, int>> path = aStarSearch(grid, start, goal);// 输出结果if (path.empty()) {cout << "No path found!" << endl;} else {cout << "Path found (" << path.size() << " steps):" << endl;for (const auto& p : path) {cout << "-> (" << p.first << "," << p.second << ") ";}cout << "\n\nVisualization:\n";printSolution(grid, path);}return 0; }
执行结果示例:
此实现完整展示了A*算法的核心机制,包含启发式函数设计、开放/封闭列表管理、路径回溯等关键环节,适合用于网格地图中的路径规划问题。
关键实现说明:
节点表示:
存储坐标、
g/h/f
值、父节点指针重载
<
运算符实现优先队列排序启发式函数:
使用欧几里得距离(满足可采纳性)
替代方案:曼哈顿距离(仅限4方向)
邻居探索:
支持8方向移动(含对角线)
直线代价1.0,对角线代价1.414(√2)
边界检查和障碍物检查
路径回溯:
通过父节点指针反向追溯路径
使用
reverse()
获得正向路径内存管理:
简化版未处理内存释放(实际应用需优化)
建议使用智能指针或对象池
注意事项和改进:
注意事项
1. 内存管理:上述代码存在内存泄漏,因为创建的节点在加入开放列表后,如果没有被及时删除,会导致内存泄漏。在实际应用中,应该使用智能指针(如`unique_ptr`)或者用一个容器管理所有节点,在函数返回前统一释放。
2. 启发式函数:这里实现了对角线距离的启发式函数,确保可采纳性。
3. 移动代价:直线移动代价为10,斜线为14(即10的倍数,避免浮点数)。
4. 节点更新:由于标准优先队列不支持更新节点值,我们采用每次有更优路径时重新加入节点(允许重复),并在取出节点时检查是否已被访问(封闭列表)来跳过旧节点。
5. 封闭列表:用于记录已处理的节点,避免重复处理。
6. 性能:在网格较大时,优先队列的操作(O(logN))和节点数量(最多网格大小)会影响性能。
改进方向
- 内存管理:使用智能指针或节点池。
- 数据结构:使用更高效的优先队列(如Fibonacci堆)来支持节点值的更新,但C++标准库中没有提供。
- 启发式函数:可以根据实际需求调整,但必须确保可采纳性(如使用欧几里得距离)。
- 路径平滑:A*找到的路径可能不是最平滑的,可以后处理进行平滑。
终极目标:
相关文章:

A*算法实现原理以及实现步骤(C++)
算法原理: A*算法是一种启发式搜索算法,用于在图中寻找最短路径。它结合了Dijkstra算法的确保最短路径的优点和贪心最佳优先搜索的高效性。其核心在于使用一个评估函数: f(n) g(n) h(n) 其中: - g(n) 表示从起点到节点n的实际代…...

Devops自动化运维---py基础篇一
python基础篇 1、基本数据类型 2、算术运算符 3、变量 变量:编程语言中能储存结果或能表示值的抽象概念 用途:用一段数据赋予一个简短、易于记忆的名字,方便重复使用3.1 格式转化变量 操作符号描述%s字符串%d整数%f浮点数 实例࿱…...

平安养老险蚌埠中心支公司开展金融宣教活动
近日,平安养老保险股份有限公司(以下简称“平安养老险”)蚌埠中心支公司,走进某合作企业开展金融教育宣传活动。 活动现场,平安养老险蚌埠中心支公司工作人员通过发放宣传手册和小礼品等方式,向企业员工普…...
游戏设计模式 - 子类沙箱
核心思想 子类沙箱模式(Subclass Sandbox)通过将核心逻辑封装在基类中,为子类提供安全的"沙箱"环境。子类通过组合或重写基类提供的预定义操作来实现行为,而非直接操作底层系统。 这种模式在游戏开发中常用于实现角色…...
java-springboot文件上传校验之只允许上传excel文件,且检查不能是脚本或者有害文件或可行性文件
四重验证机制: 文件扩展名检查(.xlsx/.xls)MIME类型检查文件魔数验证(真实文件类型)可执行文件特征检测 防御措施: 使用try-with-resources确保流关闭限制文件大小防止DoS攻击使用Apache POI的FileMagic进…...
openvino如何在c++中调用pytorch训练的模型
步骤1:将PyTorch模型转换为ONNX格式 转换代码示例(Python) import torch import torchvision1. 加载训练好的PyTorch模型 model torchvision.models.resnet18(pretrainedTrue) model.eval() # 设置为评估模式2. 创建虚拟输入(…...

Redisson简明教程—你家的锁芯该换了
1.简介 各位攻城狮们,你还在使用原生命令来上锁么?看来你还是不够懒,饺子都给你包好了,你非要吃大饼配炒韭菜,快点改善一下“伙食”吧,写代码也要来点幸福感。今天咱们就来聊聊Redisson提供的各种锁&#…...

48V带极性反接保护-差共模浪涌防护方案
在工业自动化(电动机驱动 / 工业机器人)、交通基础设施(充电桩 / 车载电子)、安防系统(监控摄像头 / 门禁)、储能设备(BMS / 离网控制器)等领域,DC48V 电源因安全特低电压…...

Python----目标检测(使用YOLO 模型进行线程安全推理和流媒体源)
一、线程安全推理 在多线程环境中运行YOLO 模型需要仔细考虑,以确保线程安全。Pythons threading 模块允许您同时运行多个线程,但在这些线程中使用YOLO 模型时,需要注意一些重要的安全问题。本页将指导您创建线程安全的YOLO 模型推理。 1.1、…...

jvm学习第1day jvm简介,栈溢出、堆溢出
jvm学习第1day jvm简介,栈溢出、堆溢出 jvm简介栈线程安全栈溢出线程运行诊断堆堆溢出 方法区方法区内存溢出常量池和运行时常量池 jvm简介 jvm 是编译后的字节码文件运行的环境, 因此各个平台有了jvm可以运行java.class文件,这是Java跨平台…...

用广告维持的免费 AI 图像生成工具(个人项目分享)
用广告维持的免费 AI 图像生成工具(个人项目分享) 免费 AI 图像生成工具网址:https://aiart.gcc.ac.cn/ 最近做了一个 AI 图像生成器,主要目标是“尽量简单”: 打开网页就能用不用注册、不用登录免费,不…...

分析Web3下数据保护的创新模式
在这个信息爆炸的时代,我们正站在 Web3 的门槛上,迎接一个以去中心化、用户主权和数据隐私为核心的新时代。Web3 不仅仅是技术的迭代,它更是一场关于数据权利和责任的结构性变革。本文将探讨 Web3 下数据保护的创新模式,以期为用户…...

减少交通拥堵、提高效率、改善交通安全的智慧交通开源了。
智慧交通视觉监控平台是一款功能强大且简单易用的实时算法视频监控系统。它的愿景是最底层打通各大芯片厂商相互间的壁垒,省去繁琐重复的适配流程,实现芯片、算法、应用的全流程组合,从而大大减少企业级应用约95%的开发成本。用户只需在界面上…...

协议融合驱动效能跃升:Modbus转Ethernet IP的挤出吹塑机应用
在现代工业自动化领域,Modbus作为一种串行通信协议,其稳定性和简单性被广泛应用于各种工控设备中。但随着技术的进步,对于更高速、更远传输距离的需求日益增长,这就需要将Modbus协议通过以太网进行传输,即实现Modbus T…...
Hive的TextFile格式优化方法
Hive的TextFile格式是一种简单的行式存储格式,数据以文本行形式存储,每行包含多个字段,字段间通过分隔符(如逗号、制表符)分隔。尽管TextFile在性能上不如ORC、Parquet等列式存储格式,但在特定场景下仍有其优势。以下是TextFile格式的特点、优势、使用场景及优化方法: …...

bug 记录 - 使用 el-dialog 的 before-close 的坑
需求说明 弹窗中内嵌一个 form 表单 原始代码 <script setup lang"ts"> import { reactive, ref } from "vue" import type { FormRules } from element-plus const ruleFormRef ref() interface RuleForm {name: stringregion: number | null } …...

Next.js 中间件鉴权绕过漏洞 CVE-2025-29927
前言:CVE-2025-29927 是一个影响 Next.js 的严重漏洞,源于开发者信任了客户端请求中携带的 X-Middleware-Rewrite 头部字段。攻击者可以手动构造该头部,实现绕过中间件逻辑,访问本应受保护的资源或 API。 影响版本:Next.js < …...

基于YOLO-NAS-Pose的无人机象群姿态估计:群体行为分析的突破
【导读】 应对气候变化对非洲象的生存威胁,本研究创新采用无人机航拍结合AI姿态分析技术,突破传统观测局限。团队在肯尼亚桑布鲁保护区对比测试DeepLabCut与YOLO-NAS-Pose两种模型,首次将后者引入野生动物研究。通过检测象群头部、脊柱等关键…...

8天Python从入门到精通【itheima】-71~72(数据容器“序列”+案例练习)
目录 71节-数据容器“序列”的切片 1.学习目标 2.什么是序列 3.序列的常用操作——切片 4.小节总结 72节——案例练习:序列的切片实践 1.案例需求 2.代码实战 好了,又一篇博客和代码写完了,励志一下吧,下一小节等等继续&a…...
中达瑞和SHIS高光谱相机在黑色水彩笔墨迹鉴定中的应用
在文件检验与物证溯源领域,对书写材料(如墨水)进行快速、准确、无损的鉴别至关重要。由陈维娜等人撰写的《高光谱技术结合化学计量法鉴别黑色水彩笔墨迹》(发表于《光谱学与光谱分析》2023年第7期)利用中达瑞和SHIS凝采…...

dvwa10——XSS(DOM)
XSS攻击: DOM型XSS 只在浏览器前端攻击触发:修改url片段代码不存储 反射型XSS 经过服务器攻击触发:可能通过提交恶意表单,连接触发代码不存储 存储型XSS 经由服务器攻击触发:可能通过提交恶意表单,连…...

dvwa14——JavaScript
LOW 先按提示尝试输入success,提交失败 那用bp抓包一下 ,抓到这些,发现有token验证,说明改对token才能过 返回页面f12看一下源码,发现value后面的值像密码,于是试一下md5和rot13的解密 ROT13加密/解密 - …...
外网访问内网服务器常用的三种简单操作步骤方法,本地搭建网址轻松让公网连接
当本地内网环境搭建部署好服务器后,怎么设置让外网公网上连接访问到呢?或本身处于不同局域网间的主机,需要进行数据交互通信,又应该如何实现操作?这些都离不开外网对内网的访问配置。 总的来说外网访问内网服务器主要…...

机器学习实验八--基于pca的人脸识别
基于pca的人脸识别 引言:pca1.pca是什么2.PCA算法的基本步骤 实例:人脸识别1.实验目的2.实现步骤3.代码实现4.实验结果5.实验总结 引言:pca 1.pca是什么 pca是一种统计方法,它可以通过正交变换将一组可能相关的变量转换成一组线…...
UDP包大小与丢包率的关系:原理分析与优化实践
文章目录 📦 UDP包大小与丢包率的关系:原理分析与优化实践一、核心结论:UDP包大小如何影响丢包率?二、技术原理解析:为什么大UDP包更容易丢失?1️⃣ MTU限制与IP分片(关键机制)2️⃣…...
ubuntu 端口复用
需求描述:复用服务器的 80端口,同时处理 ssh 和 http 请求,也就是 ssh 连接和 http 访问服务器的时候都可以指定 80 端口,然后服务器可以正确分发请求给 ssh 或者 http。 此时,ssh 监听的端口为 22,而 htt…...
Registry和docker有什么关系?
当遇到多个服务器需要同时传docker镜像的时候,一个一个的传效率会非常慢且压力完全在发送方的网络带宽;可以参考git hub,通常我们会用git push将代码传到git hub,如果谁需要代码用git pull就可以拉到自己的机器上,dock…...
C++11实现TCP网络通讯服务端处理逻辑简化版
以下是使用C11实现的TCP服务端处理逻辑,包含循环读取数据、帧头检测(AABBCC)及4376字节数据包处理: cpp #include <iostream>#include <vector>#include <cstring>#include <unistd.h>#include <arp…...
python3.9带 C++绑定的基础镜像
FROM ubuntu:20.04 # 设置非交互式环境变量(避免apt安装时提示时区选择) ENV DEBIAN_FRONTENDnoninteractive RUN ln -fs /usr/share/zoneinfo/Asia/Shanghai /etc/localtime # 安装基础编译工具和依赖 # 添加Python 3.9 PPA并安装依赖 RUN apt-get upda…...
Elasticsearch中的语义搜索(Semantic Search)介绍
Elasticsearch中的**语义搜索(Semantic Search)**是一种基于文本语义理解的搜索技术,它能够超越传统的关键词匹配,识别查询与文档之间的语义相关性,从而提供更精准、更符合用户意图的搜索结果。这种技术通过捕捉文本背后的含义、上下文和概念关联,解决了传统搜索中常见的…...