提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
A*寻路算法
概念
思路
过程
完整代码
三种算法的差别及应用场景
概念
通过计算和量化行走的各个方向的代价,来选择最优路径
代价公式:
f = g+h;
f:最终代价
g:当前代价
h:预估代价(有两种计算方法:欧拉距离和曼哈顿距离)
欧拉距离:(begin.x-end.x)^2 + (begin.y-end.y) ^2 然后开方
曼哈顿距离:x = |begin.x-end.x|; y = |begin.y -end.y|,然后x+y
思路
建立一颗树,树里面有坐标和代价值以及父节点和子节点,通过遍历8个方向算出代价,比较代价值选择方向,将结点放入树中,然后建立一个容器放每次选择方向的结点,并将当前结点进行标记走过(不标记的话会进入死循环),如果遇到死路就删除当前结点退回到代价最小的结点(这里删除的结点都是容器里的哈),直到找到终点或者容器为空结束(说明全是死路)。
过程
第一步
第二步
之后以此类推,直到寻找到目的地或者没路可以走。
完整代码
#include "num_type.h"
using namespace NUM_TYPE;
const int ROW = 10;
const int COL = 10;
const int ZXDJ = 10; //直线代价
const int XXDJ = 14; //斜线代价
enum Dir{p_up,p_down,p_left,p_right,p_lup,p_ldown,p_rup,p_rdown};
struct Mypoint
{
int row;
int col;
int f, g, h;//f:总代价,g:当前代价,h:预估代价
bool operator==(const Mypoint& pos)
{
return (pos.row == row && pos.col == col);
}
void GetH(const Mypoint& Begpos, const Mypoint& Endpos);//算代价
void GetF()
{
f = g + h;
}
};
//树结构存储节点
struct TreeNode
{
Mypoint pos;
TreeNode* pParent;
vector<TreeNode*> pChild; //存储多个孩子节点的容器
//构造函数
TreeNode(const Mypoint& pos)
{
this->pos = pos;
pParent = nullptr;
}
};
BOOL CanWalk(INUM map[ROW][COL], BOOL pathMap[ROW][COL], const Mypoint& pos);
int main()
{
INUM map[ROW][COL] =
{
{0,0,0,0,1,0,0,0,0,0},
{0,0,0,0,1,0,0,0,0,0},
{0,0,0,1,1,0,1,0,0,0},
{0,0,0,0,1,0,1,0,0,0},
{0,0,0,0,1,0,1,0,0,0},
{0,0,1,0,1,0,0,0,0,0},
{0,0,0,0,1,0,0,0,0,0},
{0,0,0,0,0,0,1,0,0,0},
{0,0,0,0,1,1,0,0,0,0},
{0,0,0,0,1,0,0,0,0,0},
};
//起始点和终点
Mypoint Begpos = { 1,1 };
Mypoint Endpos = { 6,5 };
//辅助地图
BOOL pathMap[ROW][COL] = { false };
//创建树根
TreeNode* pRoot = new TreeNode(Begpos);
vector<TreeNode*> buff; //存储数组
vector<TreeNode*>::iterator itMin;//数组的迭代器
TreeNode* pCurrent = pRoot; //记录当前节点
TreeNode* pTemp = nullptr; //试探节点
BOOL isFindEnd = false;
//开始寻路,
while (1)
{
//1. 某个点八个方向依次遍历 计算g代价
for (int i = 0; i < 8; ++i)
{
//new一个新的结点
pTemp = new TreeNode(pCurrent->pos);
switch (i)
{
case p_up:
pTemp->pos.row--;
pTemp->pos.g += ZXDJ;
break;
case p_down:
pTemp->pos.row++;
pTemp->pos.g += ZXDJ;
break;
case p_left:
pTemp->pos.col--;
pTemp->pos.g += ZXDJ;
break;
case p_right:
pTemp->pos.col++;
pTemp->pos.g += ZXDJ;
break;
case p_lup:
pTemp->pos.row--;
pTemp->pos.col--;
pTemp->pos.g += XXDJ;
break;
case p_ldown:
pTemp->pos.row++;
pTemp->pos.col--;
pTemp->pos.g += XXDJ;
break;
case p_rup:
pTemp->pos.row--;
pTemp->pos.col++;
pTemp->pos.g += XXDJ;
break;
case p_rdown:
pTemp->pos.row++;
pTemp->pos.col++;
pTemp->pos.g += XXDJ;
break;
}
//判断他们能不能走,能走的计算h及f 入树 存储在buff数组
if (CanWalk(map, pathMap, pTemp->pos))
{ //能走
//计算代价
pTemp->pos.GetH(Begpos, Endpos);
pTemp->pos.GetF();
//放入树中
pCurrent->pChild.push_back(pTemp);
pTemp->pParent = pCurrent;
//存入数组
buff.push_back(pTemp);
//标记走过这个点
pathMap[pTemp->pos.row][pTemp->pos.col] = true;
}
else
{
//不能走则直接删除这个节点
delete pTemp;
pTemp = nullptr;
}
}
// 找出数组中存储的最小代价的节点并删除
itMin = buff.begin(); //找最小
for (auto it = buff.begin(); it != buff.end(); ++it)
{
itMin = ((*itMin)->pos.f < (*it)->pos.f) ? itMin : it;
}
//移动
pCurrent = *itMin;
//删除最小代价节点
buff.erase(itMin);
//有没有到达终点
if (pCurrent->pos == Endpos)
{
isFindEnd = true;
break;
}
//没有终点,自然一直删除节点,则buff为空
if (buff.size() == 0)
{
break;
}
}
if (isFindEnd)
{
cout << "找到终点了!\n";
while (pCurrent)
{
cout << "(" << pCurrent->pos.row << "," << pCurrent->pos.col << ")";
pCurrent = pCurrent->pParent;
}
}
else
{
cout << "没有找到终点!\n";
}
return 0;
}
BOOL CanWalk(INUM map[ROW][COL], BOOL pathMap[ROW][COL], const Mypoint& pos)
{
//如果越界
if (pos.row < 0 || pos.col < 0 || pos.row >= ROW || pos.col >= COL)
{
return false;
}
//如果是墙
if (map[pos.row][pos.col])
{
return false;
}
//如果已经走过
if (pathMap[pos.row][pos.col])
{
return false;
}
return true;
}
void Mypoint::GetH(const Mypoint& Begpos, const Mypoint& Endpos)
{
int temp_x = (Endpos.col > Begpos.col) ? (Endpos.col - Begpos.col) : (Begpos.col - Endpos.col);
int temp_y = (Endpos.row > Begpos.row) ? (Endpos.row - Begpos.row) : (Begpos.row - Endpos.row);
h = temp_x + temp_y;
return;
}
三种算法的差别及应用场景
深度寻路算法:是一种盲目式搜索,选择一个方向一直往下走,直到找到终点或者遇到死路返回(栈).
广度寻路算法:是一种盲目式搜索,一层一层的去遍历,只到找到终点或者遍历完(队列)。
A*寻路算法:是一种启发式搜索,根据代价进行寻找(二叉堆)。