A*算法寻路(C++代码实现)
A*(A-Star)算法是一种静态路网中求解最短路径最有效的直接搜索方法,也是解决许多搜索问题的有效算法。算法中的距离估算值与实际值越接近,最终搜索速度越快。——来自百度百科。
我在网上看了不少关于A*寻路的文章,基本都能看懂。但是大多数文章中没有代码实现,或者是一些我不会的某些语言,还有的代码注释太少了而且太长,我看着看着就看不下去了。所以我就自己写了A*算法寻路的C++代码。
A*寻路
A*寻路算法详解推荐阅读博文链接:http://www.cppblog.com/mythit/archive/2009/04/19/80492.aspx
A*寻路的讲解文章网上很多,有的文章讲的很好,有图可以让读者一步步理解。其中我本人觉得这篇文章讲的最好,这篇文章应该是翻译的英文原文。我觉得我再写关于A*寻路的讲解肯定也没人家写的条理清晰,索性干脆推荐大家读别人的文章。但是,它只有讲解,没有代码,所以想看代码的同学就可以看本文的C++代码。
根据那篇文章,我对下面的代码做一些简单介绍:
- 每个节点我们定义成一个类,其中包含坐标x、y,评估函数F、G、H,它们的含义同原文一致。
- 定义一个三维数组path,用于存储每个位置的方格对应的“父方格”的坐标。
- 二维数组valF保序每个方格目前情况下最小的F值。
- 由于每次需要从open表中弹出的是F值最小的节点,我们选择使用优先队列来作为open表。
- 定义visit二维数组作为close表,初始值false,对应位置为true时表示已经加入close表。
具体代码中的每个操作的含义请看代码中的注释。
C++代码
本人水平较浅,代码质量不高,但是我觉得帮助理解A*算法应该没问题。
#include<iostream>
#include<algorithm>
#include<string>
#include<vector>
#include<cmath>
#include<queue>
#define N 6 // 棋盘/迷宫 的阶数
using namespace std;
class Node
{
public:
int x, y; // 节点所在位置
int F, G, H; // G:从起点开始,沿着产的路径,移动到网格上指定方格的移动耗费。
// H:从网格上那个方格移动到终点B的预估移动耗费,使用曼哈顿距离。
// F = G + H
Node(int a, int b):x(a), y(b){}
// 重载操作符,使优先队列以F值大小为标准维持堆
bool operator < (const Node &a) const
{
return F > a.F;
}
};
// 定义八个方向
int dir[8][2] = {{-1,-1}, {-1, 0}, {-1, 1}, {0, -1},
{0, 1}, {1, -1}, {1, 0}, {1, 1}};
// 优先队列,就相当于open表
priority_queue<Node>que;
// 棋盘
int qp[N][N] = { {0,0,0,0,0,0},
{0,1,1,0,1,1},
{0,0,1,0,0,0},
{0,0,1,1,1,0},
{0,1,1,0,0,0},
{1,1,0,0,0,0} };
bool visit[N][N]; // 访问情况记录,close表
int valF[N][N]; // 记录每个节点对应的F值
int path[N][N][2]; // 存储每个节点的父节点
int Manhuattan(int x, int y, int x1, int y1); // 计算曼哈顿距离
bool NodeIsLegal(int x, int y, int xx, int yy); // 判断位置合法性
void A_start(int x0, int y0, int x1, int y1); // A*算法
void PrintPath(int x1, int y1); // 打印路径
/* ----------------主函数------------------- */
int main()
{
fill(visit[0], visit[0]+N*N, false); // 将visit数组赋初值false
fill(valF[0], valF[0]+N*N, 0); // 初始化F全为0
fill(path[0][0], path[0][0]+N*N*2, -1); // 路径同样赋初值-1
// // 起点 // 终点
int x0, y0, x1, y1;
cout<<"输入起点:";
cin>>x0>>y0;
cout<<"输入终点:";
cin>>x1>>y1;
x0--; y0--; x1--; y1--;
if(!NodeIsLegal(x0, y0, x0, y0))
{
cout<<"非法起点!"<<endl;
return 0;
}
A_start(x0, y0, x1, y1); // A*算法
PrintPath(x1, y1); // 打印路径
}
/* ----------------自定义函数------------------ */
void A_start(int x0, int y0, int x1, int y1)
{
// 初始化起点
Node node(x0, y0);
node.G = 0;
node.H = Manhuattan(x0, y0, x1, y1);
node.F = node.G + node.H;
valF[x0][y0] = node.F;
// 起点加入open表
que.push(node);
while(!que.empty())
{
Node node_top = que.top(); que.pop();
visit[node_top.x][node_top.y] = true; // 访问该点,加入closed表
if(node_top.x == x1 && node_top.y == y1) // 到达终点
break;
// 遍历node_top周围的8个位置
for(int i=0; i<8; i++)
{
Node node_next(node_top.x + dir[i][0], node_top.y + dir[i][1]); // 创建一个node_top周围的节点
// 该节点坐标合法 且 未加入close表
if(NodeIsLegal(node_next.x, node_next.y, node_top.x, node_top.y) && !visit[node_next.x][node_next.y])
{
// 计算从起点并经过node_top节点到达该节点所花费的代价
node_next.G = node_top.G + int(sqrt(pow(dir[i][0],2)+pow(dir[i][1],2))*10);
// 计算该节点到终点的曼哈顿距离
node_next.H = Manhuattan(node_next.x, node_next.y, x1, y1);
// 从起点经过node_top和该节点到达终点的估计代价
node_next.F = node_next.G + node_next.H;
// node_next.F < valF[node_next.x][node_next.y] 说明找到了更优的路径,则进行更新
// valF[node_next.x][node_next.y] == 0 说明该节点还未加入open表中,则加入
if(node_next.F < valF[node_next.x][node_next.y] || valF[node_next.x][node_next.y] == 0)
{
// 保存该节点的父节点
path[node_next.x][node_next.y][0] = node_top.x;
path[node_next.x][node_next.y][1] = node_top.y;
valF[node_next.x][node_next.y] = node_next.F; // 修改该节点对应的valF值
que.push(node_next); // 加入open表
}
}
}
}
}
void PrintPath(int x1, int y1)
{
if(path[x1][y1][0] == -1 || path[x1][y1][1] == -1)
{
cout<<"没有可行路径!"<<endl;
return;
}
int x = x1, y = y1;
int a, b;
while(x != -1 || y != -1)
{
qp[x][y] = 2; // 将可行路径上的节点赋值为2
a = path[x][y][0];
b = path[x][y][1];
x = a;
y = b;
}
// □表示未经过的节点, █表示障碍物, ☆表示可行节点
string s[3] = {"□", "█", "☆"};
for(int i=0; i<N; i++)
{
for(int j=0; j<N; j++)
cout<<s[qp[i][j]];
cout<<endl;
}
}
int Manhuattan(int x, int y, int x1, int y1)
{
return (abs(x - x1) + abs(y - y1)) * 10;
}
bool NodeIsLegal(int x, int y, int xx, int yy)
{
if(x < 0 || x >= N || y < 0 || y >= N) return false; // 判断边界
if(qp[x][y] == 1) return false; // 判断障碍物
// 两节点成对角型且它们的公共相邻节点存在障碍物
if(x != xx && y != yy && (qp[x][yy] == 1 || qp[xx][y] == 1)) return false;
return true;
}
A*算法寻路(C++代码实现)的更多相关文章
- 炸弹人游戏开发系列(7):加入敌人,使用A*算法寻路
前言 上文中我们实现了炸弹人与墙的碰撞检测,以及设置移动步长来解决发现的问题.本文会加入1个AI敌人,敌人使用A*算法追踪炸弹人. 本文目的 加入敌人,追踪炸弹人 本文主要内容 开发策略 加入敌人 实 ...
- 对一致性Hash算法,Java代码实现的深入研究
一致性Hash算法 关于一致性Hash算法,在我之前的博文中已经有多次提到了,MemCache超详细解读一文中"一致性Hash算法"部分,对于为什么要使用一致性Hash算法.一致性 ...
- 一道算法题目, 二行代码, Binary Tree
June 8, 2015 我最喜欢的一道算法题目, 二行代码. 编程序需要很强的逻辑思维, 多问几个为什么, 可不可以简化.想一想, 二行代码, 五分钟就可以搞定; 2015年网上大家热议的 Home ...
- 数据关联分析 association analysis (Aprior算法,python代码)
1基本概念 购物篮事务(market basket transaction),如下表,表中每一行对应一个事务,包含唯一标识TID,和购买的商品集合.本文介绍一种成为关联分析(association a ...
- AC-BM算法原理与代码实现(模式匹配)
AC-BM算法原理与代码实现(模式匹配) AC-BM算法将待匹配的字符串集合转换为一个类似于Aho-Corasick算法的树状有限状态自动机,但构建时不是基于字符串的后缀而是前缀.匹配 时,采取自后向 ...
- 利用朴素贝叶斯算法进行分类-Java代码实现
http://www.crocro.cn/post/286.html 利用朴素贝叶斯算法进行分类-Java代码实现 鳄鱼 3个月前 (12-14) 分类:机器学习 阅读(44) 评论(0) ...
- 对一致性Hash算法,Java代码实现的深入研究(转)
转载:http://www.cnblogs.com/xrq730/p/5186728.html 一致性Hash算法 关于一致性Hash算法,在我之前的博文中已经有多次提到了,MemCache超详细解读 ...
- SSD算法及Caffe代码详解(最详细版本)
SSD(single shot multibox detector)算法及Caffe代码详解 https://blog.csdn.net/u014380165/article/details/7282 ...
- 【转载】对一致性Hash算法,Java代码实现的深入研究
原文地址:http://www.cnblogs.com/xrq730/p/5186728.html 一致性Hash算法 关于一致性Hash算法,在我之前的博文中已经有多次提到了,MemCache超详细 ...
随机推荐
- oracle查询用户所在表为空的表名
1 select * from user_tables where num_rows = 0 查询之后发现查询的不全,查询之后发现num_rows为空 select * from user_table ...
- Centos 8 误删/boot文件夹下文件后的恢复
一.环境 Centos8 二.过程 1.查看/boot底下文件 ls /boot 2.模拟误删/boot底下文件 rm -rf /boot/*ls /boot 3.reboot,无法正常启动 4.在v ...
- 关于easyswoole实现websocket聊天室的步骤解析
在去年,我们公司内部实现了一个聊天室系统,实现了一个即时在线聊天室功能,可以进行群组,私聊,发图片,文字,语音等功能,那么,这个聊天室是怎么实现的呢?后端又是怎么实现的呢? 后端框架 在后端框架上,我 ...
- Leetcode 递归题
24. 两两交换链表中的节点 题目描述: 给定一个链表,两两交换其中相邻的节点,并返回交换后的链表. 你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换. 示例: 给定 1->2-&g ...
- Java 在Word中创建邮件合并模板并合并文本和图片
Word里面的邮件合并功能是一种可以快速批量操作同类型数据的方式,常见的如数据填充.打印等.其中必不可少的步骤包括用于填充的模板文档.填充的数据源以及实现邮件合并的功能.下面,通过Java程序展示如何 ...
- OpenResty简介
OpenResty(也称为 ngx_openresty)是一个全功能的 Web 应用服务器.它打包了标准的 Nginx 核心,很多的常用的第三方模块,以及它们的大多数依赖项. 通过揉和众多设计良好的 ...
- final修饰符(5)-final方法
final修饰的类的方法不能被重写,例如如果父类不希望子类重写某个方法,则可以使用final修饰符修饰该方法 在java的Object类里面有一个final方法:getClass(),因为Java不希 ...
- Map集合笔记
一.Map集合的特点 Map集合是一个双列集合 Map中的元素,key和value的数据类型可以相同,也可以不同. Map中的元素,key是允许重复的,value是可以重复的 Map中的元素,key和 ...
- 计算机毕业设计选题大合集,含ssm,springboot,小程序,php,python
1基于springboot医院急诊系统 2基于springboot校园闲置物品租售系统 3基于springboot校园闲置物品交易网站 4基于springboot图书网站 5基于springboot外 ...
- 【算法学习笔记】概率与期望DP
本文学习自 Sengxian 学长的博客 之前也在CF上写了一些概率DP的题并做过总结 建议阅读完本文再去接着阅读这篇文章:Here 前言 单纯只用到概率的题并不是很多,从现有的 OI/ACM 比赛中 ...