A星寻路算法
我们在知道了广度寻路算法(循环嵌套开销大)和深度寻路算法(不一定能找到最佳路径)的优缺点后,我们开始学习A星寻路算法。
A星寻路算法:他的优点是能找到最短路径,不需要回退,没有广度寻路算法那么大的开销
而他的核心逻辑就是:量化评估
量化评估这点很重要,简单来说就是代价,万事万物都有代价,而量化评估就是由你来决定这个代价的值(这个值不能离谱,需要合乎实际)。
如,用数字表示,不能太离谱了
现在假设代价是: 直线走1格代价10 ->5
斜线走1格代价14 ->7
为什么是这个数呢,原因是:
红色的为出发点,则要到右上角这个点,走的斜线和走右边的直线一画,一个等腰直角三角形就出现了。
那么他们的比例要符合直角边与斜边的比例,即1:1.414(根号二)
如果这个代价设定是不合理的,得到的结果就会和想要得到的结果背道而驰。
代价的设置是十分重要。
最后就是F=g+h
F:最终用来评估的代价
G:起点到当前点,已经付出的代价
g是会随着移动而增加
H:当前点到终点预估代价
h的计算:无视障碍到终点距离,是会随着移动而改变(离终点越近,这个越小)
算出来之后,哪个f的值最小就是最佳路径
相对的细节也需要完善,要准备一个数组存储这些f,当其走过则删除(必然是删除最小的)【假如这个时候最小的路程会遇到墙壁,因为已经删除了,所以会选择另一个最小代价的去】
然后从数组里面查找另一个最小的,然后就可以从这个最小的开始继续寻路
以下是源码(内涵注释):
#include <iostream>
#include<vector>
using namespace std;
#define ROW 10
#define COL 10
enum Direction
{
p_up,
p_down,
p_left,
p_right,
p_leftDown,
p_rightDown,
p_leftUp,
p_rightUp,
};
#define ZXprice 10 //直线代价
#define XXprice 14 //斜线代价
struct myPoint
{
int row, col;
int f, g, h;
void getH(int r, int c);
void getF()
{
f = g + h;
}
bool operator==(const myPoint& p)
{
return (row == p.row && col == p.col);
}
};
//做树
struct TreeNode
{
myPoint pos;
TreeNode* pParent;
vector<TreeNode*> pChild;
TreeNode(const myPoint& p)
{
pos = p;
pParent = NULL;
}
};
//能不能走
bool canWork(int map[ROW][COL],bool pathMap[ROW][COL], myPoint pos)
{
//是否越界
if (pos.row<0||pos.row>=ROW||pos.col<0||pos.col>=COL)
{
return false;
}
//是否是墙
else if (1 == map[pos.row][pos.col])
{
return false;
}
//是否走过
else if (pathMap[pos.row][pos.col] == true)
{
return false;
}
return true;
}
int main()
{
int map[ROW][COL] = {
{0,0,0,0,1,1,1,0,0,0},
{0,0,0,0,1,0,0,0,0,0},
{0,0,0,0,1,0,0,0,0,0},
{0,0,0,0,1,0,0,0,0,0},
{0,0,0,0,1,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,1,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,1,0,0,0,0,0},
{0,0,0,0,1,0,0,0,0,0},
};
bool pathMap[ROW][COL] = { 0 };
myPoint beginPos = { 2,1 };//起点设置
myPoint endPos = { 9,9 };//终点设置
pathMap[beginPos.row][beginPos.col] = true;//走过起点
//创建树的根节点
TreeNode* pRoot = new TreeNode(beginPos);
vector<TreeNode*> buff;//数组存储
//迭代器
vector<TreeNode*>::iterator it;
vector<TreeNode*>::iterator MinIt;
TreeNode* pCurrent = pRoot;
TreeNode* pChild = NULL;
bool isFindEnd = false;
while (1)//循环寻路
{
//1.寻找当前点周围能走的点
for (int i = 0; i < 8; i++)
{
pChild = new TreeNode(pCurrent->pos);
switch (i)
{
case p_up:pChild->pos.row--; pChild->pos.g += ZXprice; break;
case p_down:pChild->pos.row++; pChild->pos.g += ZXprice; break;
case p_left:pChild->pos.col--; pChild->pos.g += ZXprice; break;
case p_right:pChild->pos.col++; pChild->pos.g += ZXprice; break;
case p_leftUp:pChild->pos.row--; pChild->pos.col--; pChild->pos.g += XXprice; break;
case p_rightUp:pChild->pos.row--; pChild->pos.col++; pChild->pos.g += XXprice; break;
case p_leftDown:pChild->pos.row++; pChild->pos.col--; pChild->pos.g += XXprice; break;
case p_rightDown:pChild->pos.row++; pChild->pos.col++; pChild->pos.g += XXprice; break;
}
//2.计算f的值并入树,buff存储
//1..先判断能不能走
if (canWork(map, pathMap, pChild->pos))//能走
{
//计算f的值
pChild->pos.getH(endPos.row, endPos.col);
pChild->pos.getF();
//入树
pCurrent->pChild.push_back(pChild);
pChild->pParent = pCurrent;
//buff存储
buff.push_back(pChild);
//标记走过的路
pathMap[pChild->pos.row][pChild->pos.col] = true;
}
else//不能走
{
delete pChild;
pChild = NULL;
}
}
//3.当前点周围都找完了,找出buff中f值最小的点,下次循环就是当前点
MinIt = buff.begin();//假设第一个最小
for ( it = buff.begin(); it != buff.end(); it++)
{
MinIt = (((*it)->pos.f < (*MinIt)->pos.f) ? it : MinIt);
}
pCurrent = *MinIt;
//4.buff数组中删除最小f
buff.erase(MinIt);
//5.判断是否找到终点了
if (pCurrent->pos==endPos)
{
isFindEnd = true;
break;
}
//如果地图找完了,没有找到终点
if (buff.size()==0)//记录数组里面的全部走完了
{
break;
}
}
//找到终点即打印
if (isFindEnd)
{
cout << "找到终点";
//对结果进行打印
while (pCurrent)//通过现在的结尾一路上走
{
cout <<"(" << pCurrent->pos.row << "," << pCurrent->pos.col << ")";
pCurrent = pCurrent->pParent;
}
}
else
{
cout << "找不到终点";
}
return 0;
}
void myPoint::getH(int r, int c)
{
int x = ((c > col) ? (c - col) : (col - c));
int y = ((r > row) ? (r - row) : (row - r));
h = (x + y) * ZXprice; //有了h,即有了F
}