数据结构和算法总结(三):A* 寻路算法
前言
复习下寻路相关的东西,而且A star寻路在游戏开发中应用挺多的,故记录下。
正文
迪杰斯特拉算法
说起A*得先谈谈Dijkstra算法,它是在BFS基础上的一种带权值的两点最短寻路贪心算法。
算法步骤
0.初始化图,输入起点,将所有点到起始点的距离设置为∞。
1.将起始点OriginNode记录为已访问,并从OriginNode开始将周围的点加入到待遍历列表中,更新到达这些点的距离,并将他们的父节点设置为起始点OriginNode。
2.如果待遍历列表不为空,则从列表中取出到达距离最小的点currentNode;反之则跳到第5步。
3.遍历currentNode邻接的点neighbors:
1) 如果已访问过,则遍历其他点。
2) 如果邻接的某点neighbor已经在待遍历列表中,则比较neighbor当前的距离distance(OriginNode,neighbor) 与 distance(OriginNode,currentNode) + distance(currentNode,neighbor), 如果后者更小,则将neighbor的距离值更新为该值并将父节点设置为currentNode。
3) 如果不在待遍历列表,则将neighbor放入待遍历列表,并则将neighbor的距离值更新为 distance(OriginNode,currentNode) + distance(currentNode,neighbor), 并将父节点设置为currentNode。
4.重复第2步
5.输入终点,从终点开始回溯父节点,打印路径。
动态过程
A* 寻路算法
A*的思路其实与Dijkstra一致,相比较Dijkstra,A*引入了一个启发公式来衡量消耗
其中 g(n) 代表从起始点到当前点的消耗,h(n) 代表终点到当前点的预估消耗(通常用曼哈顿距离或者欧拉距离衡量,这里不赘述,参考)。
算法思路
/*
A*寻路算法 带权值的BFS
每个点保存达到该点的消耗F = G值(出发点到当前点的代价值) + H值(目标点到当前点的预估距离,常用曼哈顿距离或者欧拉距离)
维护着两个列表,开放列表和关闭列表 逻辑:
初始化列表Close,Open,Path,将StartNode放入Open列表中
while(true)
{
从Open表中取得F值最小的节点CurrentNode.
从Open表中删除CurrentNode
将CurrentNode加入Close表中
if ( CurrentNode 是 目标点targetNode)
{
从CurrentNode点回溯直到起点并将所有回溯的节点加入Path表
打印Path表
return;
}
for (CurrentNode 的每一个邻接点 neighbor)
{
if(neighbor 在 Close表中 || neighbor 不可通行)
{
continue;
}
if(neighbor 在 Open表中)
{
if( CurrentNode 到达neighbor的消耗costG + node的G值 < neighbor的G值)
{
更新neighbor的G值;
将neighbor的回溯节点fatherNode设置为CurrentNode;
}
}
else
{
更新neighbor的G值;
更新neighbor的H值;
将neighbor的回溯节点fatherNode设置为CurrentNode;
将neighbor放入Open列表;
}
}
}
*/
动态过程
代码:
AstarNode的定义实现.
Astar.h
#pragma once
#include <vector>
#include <math.h> using std::vector; class AstarNode
{
private:
int m_igValue;
int m_ihValue;
int m_ifValue;
AstarNode* m_father;
public:
int x;
int y;
bool isPass;
public:
void SetG(int iValue);
int GetG() const;
int GetH() const;
int GetF() const;
void CalculateH(const AstarNode* endNode);
void SetFather(AstarNode* node);
AstarNode* GetFather() const;
bool isInList(const vector<AstarNode*>& nodeList) const;
public:
AstarNode();
AstarNode(int x,int y);
AstarNode(const AstarNode& node);
~AstarNode();
AstarNode& operator=(const AstarNode& node);
};
Astar.cpp
#include "Astar.h" AstarNode::AstarNode()
{
isPass = true;
m_igValue = 0;
m_ihValue = 0;
m_ifValue = 0;
m_father = nullptr;
} AstarNode::AstarNode(int x,int y)
{
isPass = true;
m_igValue = 0;
m_ihValue = 0;
m_ifValue = 0;
m_father = nullptr;
this->x = x;
this->y = y;
} AstarNode::AstarNode(const AstarNode& node)
{
isPass = node.isPass;
m_igValue = node.GetG();
m_ihValue = node.GetH();
m_ifValue = node.GetF();
m_father = node.GetFather();
this->x = node.x;
this->y = node.y;
} AstarNode& AstarNode::operator=(const AstarNode& node)
{
isPass = node.isPass;
m_igValue = node.GetG();
m_ihValue = node.GetH();
m_ifValue = node.GetF();
m_father = node.GetFather();
this->x = node.x;
this->y = node.y;
return *this;
} AstarNode::~AstarNode()
{ } void AstarNode::SetG(int iValue)
{
m_igValue = iValue;
} int AstarNode::GetG() const
{
return m_igValue;
} int AstarNode::GetF() const
{
return m_igValue + m_ihValue;
} int AstarNode::GetH() const
{
return m_ihValue;
} void AstarNode::CalculateH(const AstarNode* endNode)
{
m_ihValue = abs(endNode->x - x) + abs(endNode->y - y);
} void AstarNode::SetFather(AstarNode* node)
{
m_father = node;
} AstarNode* AstarNode::GetFather() const
{
return m_father;
} bool AstarNode::isInList(const vector<AstarNode*>& nodeList) const
{
for(auto iter = nodeList.begin();iter != nodeList.end();iter++)
{
if((*iter)->x == this->x && (*iter)->y == this->y)
return true;
}
return false;
}
main.cpp
#include "Astar.cpp"
#include <algorithm>
using namespace std; /*
'e' 终点
's' 起点
'#' 障碍物
'-' 可通行点
'o' 回溯路径节点
*/ vector<vector<char>> graph = {
{'-', '-', '-', '-', '-', '-', '-'},
{'-', '-', 'e', '#', '-', '-', '-'},
{'-', '#', '#', '#', '#', '-', '-'},
{'-', '-', '-', '-', '#', '#', '-'},
{'-', '-', '#', '-', '#', '-', '-'},
{'-', '-', '-', '#', '-', '-', '-'},
{'-', '-', '-', '-', '-', '-', 's'},
}; void BuildGraph(vector<vector<AstarNode *>> &astarGraph, AstarNode &start, AstarNode &end)
{
for (int i = 0; i < astarGraph.size(); i++)
{
auto row = astarGraph[i];
for (int j = 0; j < row.size(); j++)
{
char mark = graph[i][j];
if (mark == 's')
{
start.x = i;
start.y = j;
start.SetG(0);
start.CalculateH(&end);
astarGraph[i][j] = &start;
}
else if (mark == 'e')
{
end.x = i;
end.y = j;
astarGraph[i][j] = &end;
}
else if (mark == '#')
{
astarGraph[i][j] = new AstarNode(i, j);
astarGraph[i][j]->isPass = false;
}
else
{
astarGraph[i][j] = new AstarNode(i, j);
}
}
}
} void printGraph()
{
for (int i = 0; i < graph.size(); i++)
{
auto line = graph[i];
for (int j = 0; j < line.size(); j++)
{
cout << line[j] << " ";
}
cout << endl;
}
} vector<AstarNode *>::iterator GetMinFNode(vector<AstarNode *> &openList)
{
auto tmp = openList.begin();
for (auto iter = openList.begin(); iter != openList.end(); iter++)
{
if ((*iter)->GetF() < (*tmp)->GetF())
{
tmp = iter;
}
}
return tmp;
} inline int GetCost(int xDiff, int yDiff)
{
if (xDiff == 0 || yDiff == 0)
return 10;
return 14;
} void SearchOneNode(AstarNode ¤tNode, AstarNode &neighbor, AstarNode &startNode, AstarNode &endNode, vector<AstarNode *> &openList, vector<AstarNode *> &closeList)
{
if (neighbor.isInList(closeList) || !neighbor.isPass)
return;
int gCost = GetCost(currentNode.x - neighbor.x, currentNode.y - neighbor.y);
if (neighbor.isInList(openList))
{
if (currentNode.GetG() + gCost < neighbor.GetG())
{
neighbor.SetG(currentNode.GetG() + gCost);
neighbor.SetFather(¤tNode);
}
}
else
{
neighbor.SetG(currentNode.GetG() + gCost);
neighbor.SetFather(¤tNode);
neighbor.CalculateH(&endNode);
openList.push_back(&neighbor);
}
} void Astar()
{
vector<AstarNode *> OpenList;
vector<AstarNode *> CloseList;
vector<AstarNode *> Path;
size_t len = graph.size();
vector<vector<AstarNode *>> astarGraph(len, vector<AstarNode *>(len)); AstarNode startNode;
AstarNode endNode; BuildGraph(astarGraph, startNode, endNode);
OpenList.push_back(&startNode);
while (!OpenList.empty())
{
auto it = GetMinFNode(OpenList);
AstarNode *currentNode = *it;
OpenList.erase(it);
CloseList.push_back(currentNode);
if (currentNode->x == endNode.x && currentNode->y == endNode.y)
{
while (currentNode->GetFather())
{
graph[currentNode->x][currentNode->y] = graph[currentNode->x][currentNode->y] == '-' ? 'o' : 'e';
Path.push_back(currentNode);
currentNode = currentNode->GetFather();
}
printGraph();
break;
}
int curX = currentNode->x;
int curY = currentNode->y;
int row = graph.size();
int column = row;
if (curX + 1 < row)
SearchOneNode(*currentNode, *astarGraph[curX + 1][curY], startNode, endNode, OpenList, CloseList);
if (curX - 1 >= 0)
SearchOneNode(*currentNode, *astarGraph[curX - 1][curY], startNode, endNode, OpenList, CloseList);
if (curY + 1 < column)
SearchOneNode(*currentNode, *astarGraph[curX][curY + 1], startNode, endNode, OpenList, CloseList);
if (curY - 1 >= 0)
SearchOneNode(*currentNode, *astarGraph[curX][curY - 1], startNode, endNode, OpenList, CloseList);
if (curX - 1 >= 0 && curY - 1 >= 0)
SearchOneNode(*currentNode, *astarGraph[curX - 1][curY - 1], startNode, endNode, OpenList, CloseList);
if (curX - 1 >= 0 && curY + 1 < column)
SearchOneNode(*currentNode, *astarGraph[curX - 1][curY + 1], startNode, endNode, OpenList, CloseList);
if (curX + 1 < row && curY - 1 >= 0)
SearchOneNode(*currentNode, *astarGraph[curX + 1][curY - 1], startNode, endNode, OpenList, CloseList);
if (curX + 1 < row && curY + 1 < row)
SearchOneNode(*currentNode, *astarGraph[curX + 1][curY + 1], startNode, endNode, OpenList, CloseList);
}
cout << endl;
} int main()
{
auto start = chrono::steady_clock::now();
Astar();
auto end = chrono::steady_clock::now();
cout << chrono::duration<double, milli>(end - start).count() << " ms" << endl;
getchar();
}
参考资料
数据结构和算法总结(三):A* 寻路算法的更多相关文章
- 【数据结构】 最小生成树(三)——prim算法
上一期介绍到了kruskal算法,这个算法诞生于1956年,重难点就是如何判断是否形成回路,此处要用到并查集,不会用当然会觉得难,今天介绍的prim算法在kruskal算法之后一年(即1957年)诞生 ...
- 人工智能: 自动寻路算法实现(三、A*算法)
博客转载自:https://blog.csdn.net/kwame211/article/details/78139506 本篇文章是机器人自动寻路算法实现的第三章.我们要讨论的是一个在一个M×N的格 ...
- [转] A*寻路算法C++简单实现
参考文章: http://www.policyalmanac.org/games/aStarTutorial.htm 这是英文原文<A*入门>,最经典的讲解,有demo演示 http: ...
- 【数据结构】 最小生成树(二)——kruskal算法
上一期说完了什么是最小生成树,这一期咱们来介绍求最小生成树的算法:kruskal算法,适用于稀疏图,也就是同样个数的节点,边越少就越快,到了数据结构与算法这个阶段了,做题靠的就是速度快,时间复杂度小. ...
- [A*算法]基于Unity实现A*算法(二)
写在前面:上一篇当时是非常简单的了解一下A*,昨天还有一些问题没解决,就暂时把自己查阅的文坛摘抄了过来(毕竟人家写的比我要好的多 :> ) 今天终于解决了,就又写了这一篇,正好我自己再梳理一遍, ...
- 图论算法(四)Dijkstra算法
最短路算法(三)Dijkstra算法 PS:因为这两天忙着写GTMD segment_tree,所以博客可能是seg+图论混搭着来,另外segment_tree的基本知识就懒得整理了-- Part 1 ...
- A*寻路算法的探寻与改良(三)
A*寻路算法的探寻与改良(三) by:田宇轩 第三分:这部分内容基于树.查找算法等对A*算法的执行效率进行了改良,想了解细 ...
- Java数据结构和算法(三)顺序存储的树结构
Java数据结构和算法(三)顺序存储的树结构 二叉树也可以用数组存储,可以和完全二叉树的节点一一对应. 一.树的遍历 // 二叉树保存在数组中 int[] data; public void preO ...
- 重读《学习JavaScript数据结构与算法-第三版》- 第4章 栈
定场诗 金山竹影几千秋,云索高飞水自流: 万里长江飘玉带,一轮银月滚金球. 远自湖北三千里,近到江南十六州: 美景一时观不透,天缘有分画中游. 前言 本章是重读<学习JavaScript数据结构 ...
随机推荐
- 工作流调度系统Azkaban的简介和使用
1 概述 1.1 为什么需要工作流调度系统 l 一个完整的数据分析系统通常都是由大量任务单元组成: shell脚本程序,java程序,mapreduce程序.hive脚本等 l 各任务单元之间存在时间 ...
- Redis集群模式之分布式集群模式
前言 Redis集群模式主要有2种: 主从集群 分布式集群. 前者主要是为了高可用或是读写分离,后者为了更好的存储数据,负载均衡. 本文主要讲解主从集群.本章主要讲解后一半部分,Redis集群. 与本 ...
- mac 设置 MySQL 数据库默认编码(字符集)为 UTF-8
mac 设置 MySQL 数据库默认编码(字符集)为 UTF-8 原文链接:https://juejin.im/post/5bbdca76e51d45021147de44 鉴于有些刚接触 MySQ ...
- 共享打印机,错误0x80070035和错误0x00000709的解决办法
这两个错误可以说是共享打印机里经常出现的错误了. 首先,要确认客户机可以ping通打印机的直连电脑的IP,如果这一步不通,那别玩了. 其次,很多人会忽略的一点儿,两个电脑的dns最好设置为相同的,经测 ...
- PAT 甲级 1012 The Best Rank (25 分)(结构体排序)
题意: 为了评估我们第一年的CS专业学生的表现,我们只考虑他们的三个课程的成绩:C - C编程语言,M - 数学(微积分或线性代数)和E - 英语.同时,我们鼓励学生强调自己的最优秀队伍 - 也就是说 ...
- django的邮件email功能
注意 测试的时候python manage.py test -p "test_tasks.py" -v 3,默认使用的EMAIL_BACKEND配置为:'django.core.m ...
- stm32第一章cortex-M3处理器概述
处理器特点 哈弗结构3级流水线内核 实现Thumb-2指令集,告别切换32位的arm指令和16位的Thumb指令,优化性能和代码密度 结合可配置的嵌套向量中段控制器Nvic,提供非屏蔽中断NMI和32 ...
- Flutter 轻量级的ToolTip控件
轻提示的效果在应用中是少不了的,其实Flutter已经准备好了轻提示控件,这就是toolTip. 轻量级操作提示 其实Flutter中有很多提示控件,比如Dialog.Snackbar和BottomS ...
- 【SQL】在数据库中发起http请求的小改进
市面上常见的是用MSXML2.ServerXMLHTTP这个类,但这个类在发起异步请求时并不可靠,就是当send后并不一定会发出这个请求.这里推荐改用Microsoft.XMLHTTP,如果只是简单的 ...
- CentOS7.5安装GitLab及汉化
一.GitLab英文版安装 1.下载gitlab安装包,然后安装 wget --content-disposition https://packages.gitlab.com/gitlab/gitla ...