A*寻路算法

        A*寻路算法是一种用于静态位置高效搜索算法。他是在平面中两点之间去寻找一条可以到达的最短路径算法。

       二维平面之中两个点,寻找可以抵达的最短路径。首先明确三个概念,H值,目前点到终点的曼哈顿距离(曼哈顿距离,就是两个位置长度差值和高度差值的和),G值,目前点到起点的消耗代价值,如果只是寻找路径,可以将该值也看成是这两点的曼哈顿距离,F值,H值和G值的和。

        今天我这里讨论的寻路算法,暂时不考虑八个方向,仅仅考虑某个点所对应的上下左右四个方向可以移动。G值处理为距离起点的曼哈顿距离。接下来可能有点绕,但是是该算法的内容核心。

        首先定义两个集合,openlist和closelist。openlist用于存放接下来可能会去的点的位置,closelist用于存放已经去过,不会再去的点的位置。然后,选定起点,将起点周边(上下左右)符合要求的点加入openlist,符合的要求需要可以达到,即没有阻挡物并且不存在于closelist中的点。在所有的openlist中找到F值最小的点,然后以该点为下一步行进点,在继续查找该行进点的上下左右符合要求的点。重复操作。结束条件,当openlist为空,没有找到终点,则不存在可以到达的路线,当行进点为终点时,则找到了最小路径。

       结合下图,屌丝找女朋友的图来看下。

 

A*寻路算法_第1张图片

        首先定义openlist和closelist。因为openlist每次需要查找最小F值,所以使用multimap结构,将F值作为键值。

struct stInfo
{
    int iF = 0;
    int iG = 0; //到起点
    int iH = 0;
    int iPosx = -1;
    int iPosy = -1;
    int iFPosx = -1;
    int iFPosy = -1;
};

std::multimap mapOpenList;

        closelist因为需要查找某个位置是否存在于closelist中,如果存在则不能在使用该点,所以以x坐标和y坐标用作键值:std::map, stInfo> mapCloseList;

        查找过程类似于一棵树的广度和深度遍历的集合,在屌丝的行进点的时候,先进行广度遍历,将所有可能的结果存放到openlist中,然后进行深度遍历,找到F值最小的一个分支,作为下一个该屌丝的行进点。同时将上一个行进点加入closelist中,以后将不再考虑该点。至于最后怎么将整条路径打印出来,上边的结构体中定义两个变量iFPosx, iFPosy,他们存放父节点的位置,比如行进点的子节点有四个,那么他们四个的父节点都是该行进点。这里可以将上边的屌丝找女朋友的图,理解成为一棵四叉树。最后结束条件:一种情况是openlist为空,那么该屌丝已经将所有的路都完了,还没有找到女朋友,即该屌丝永远找不到女朋友。另一种情况是行进点是女朋友的点,那么该屌丝找到了女朋友。

代码:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define _CRT_SECURE_NO_WARNINGS
using namespace std;


struct stInfo
{
	int iF = 0;
	int iG = 0; //到起点
	int iH = 0;
	int iPosx = -1;
	int iPosy = -1;
	int iFPosx = -1;
	int iFPosy = -1;
};
/*
*  brief 获取两个点的曼哈顿距离
*/
int getDistanceTwoPoint(int iPosxFrom, int iPosyFrom, int iPosxTo, int iPosyTo)
{
	return abs(iPosxFrom - iPosxTo) + abs(iPosyFrom - iPosyTo);
}
/*
*  brief 构建stInfo
*/
void buildStInfo(stInfo &stTmp, int iPosx, int iPosy, std::pair posBegin, std::pair posEnd, int iFPosx = -1, int iFPosy = -1)
{
	stTmp.iPosx = iPosx;
	stTmp.iPosy = iPosy;
	stTmp.iFPosx = iFPosx;
	stTmp.iFPosy = iFPosy;
	stTmp.iG = getDistanceTwoPoint(iPosx, iPosy, posBegin.first, posBegin.second);
	stTmp.iH = getDistanceTwoPoint(iPosx, iPosy, posEnd.first, posEnd.second);
	stTmp.iF = stTmp.iG + stTmp.iH;
	//cout << "iposx:" << iPosx << " iposy:" << iPosy << " iG:" << stTmp.iG << " iH:" << stTmp.iH << " iF:" << stTmp.iF << endl;
}
/*
*  brief 检查位置合法
*/
bool isLegalPos(int **pstAr, int iPosx, int iPosy)
{
	//cout << "isLegalPos  " << iPosx << "  " << iPosy << endl;
	return (iPosx >= 0 && iPosx < 5 && iPosy >= 0 && iPosy < 5) && (pstAr[iPosx][iPosy] != 1);
}
/*
*  brief 检查某个点不在openlist
*/
bool notInOpenList(int iPosx, int iPosy, int iF, std::multimap &mapOpenList)
{
	int iCount = mapOpenList.count(iF);
	auto it = mapOpenList.find(iF);
	while (it != mapOpenList.end() && iCount)
	{
		if (it->second.iPosx == iPosx && it->second.iPosy == iPosy)
			return false;
		++it;
		--iCount;
	}
	return true;
}
/*
*  brief 某个位置周围四个符合要求的点插入openlist
*/
bool insertPointToOpenList(int **pstAr, const std::pair &posNow, std::multimap &mapOpenList, std::map, stInfo> &mapCloseList,
	const std::pair &posBegin, const std::pair &posEnd)
{
	if (isLegalPos(pstAr, posNow.first - 1, posNow.second) && mapCloseList.find(std::make_pair(posNow.first - 1, posNow.second)) == mapCloseList.end())
	{
		stInfo stTmp;
		buildStInfo(stTmp, posNow.first-1, posNow.second, posBegin, posEnd, posNow.first, posNow.second);
		if (notInOpenList(posNow.first - 1, posNow.second, stTmp.iF, mapOpenList))
		{
			mapOpenList.insert(std::make_pair(stTmp.iF, stTmp));
			if (stTmp.iH == 0)
				return true;
		}
	}
	if (isLegalPos(pstAr, posNow.first, posNow.second-1) && mapCloseList.find(std::make_pair(posNow.first, posNow.second-1)) == mapCloseList.end())
	{
		stInfo stTmp;
		buildStInfo(stTmp, posNow.first, posNow.second-1, posBegin, posEnd, posNow.first, posNow.second);
		if (notInOpenList(posNow.first, posNow.second-1, stTmp.iF, mapOpenList))
		{
			mapOpenList.insert(std::make_pair(stTmp.iF, stTmp));
			if (stTmp.iH == 0)
				return true;
		}
	}
	if (isLegalPos(pstAr, posNow.first + 1, posNow.second) && mapCloseList.find(std::make_pair(posNow.first + 1, posNow.second)) == mapCloseList.end())
	{
		stInfo stTmp;
		buildStInfo(stTmp, posNow.first+1, posNow.second, posBegin, posEnd, posNow.first, posNow.second);
		if (notInOpenList(posNow.first+1, posNow.second, stTmp.iF, mapOpenList))
		{
			mapOpenList.insert(std::make_pair(stTmp.iF, stTmp));
			if (stTmp.iH == 0)
				return true;
		}
	}
	if (isLegalPos(pstAr, posNow.first, posNow.second+1) && mapCloseList.find(std::make_pair(posNow.first, posNow.second+1)) == mapCloseList.end())
	{
		stInfo stTmp;
		buildStInfo(stTmp, posNow.first, posNow.second+1, posBegin, posEnd, posNow.first, posNow.second);
		if (notInOpenList(posNow.first, posNow.second+1, stTmp.iF, mapOpenList))
		{
			mapOpenList.insert(std::make_pair(stTmp.iF, stTmp));
			if (stTmp.iH == 0)
				return true;
		}
	}
	return false;
}
/*
*  brief A*寻路
*/
void findRoad(int **pstAr, std::pair posBegin, std::pair posEnd)
{
	std::multimap mapOpenList;
	std::map, stInfo> mapCloseList;
	std::pair posNow = posBegin;

	//起点加入Openlist
	stInfo stTmp;
	buildStInfo(stTmp, posNow.first, posNow.second, posBegin, posEnd);
	mapOpenList.insert(std::make_pair(stTmp.iF, stTmp));

	while (1)
	{
		auto itopen = mapOpenList.begin();
		if (itopen == mapOpenList.end())   //openlist为空,无路径      如果不为空,取第一个就是iF最小值
		{
			cout << "无路径" << endl;
			return;
		}

		posNow = std::make_pair(itopen->second.iPosx, itopen->second.iPosy);
		//cout << "====" << posNow.first << "  " << posNow.second << endl;

		stInfo stTmp = itopen->second;
		mapOpenList.erase(itopen);
		mapCloseList.insert(std::make_pair(std::make_pair(stTmp.iPosx, stTmp.iPosy), stTmp));

		if (stTmp.iH == 0)
		{
			cout << "找到了" << endl;
			break;
		}

		insertPointToOpenList(pstAr, posNow, mapOpenList, mapCloseList, posBegin, posEnd);
		//if (insertPointToOpenList(pstAr, posNow, mapOpenList, mapCloseList, posBegin, posEnd)) //周围坐标加进去
		{
		//	mapCloseList.insert(std::make_pair(std::make_pair(posEnd.first, posEnd.second),stInfo()));
		//	cout << "找到了" << endl;
		//	break;
		}
	}

	posNow = std::make_pair(posEnd.first, posEnd.second);
	while (1)
	{
		auto it = mapCloseList.find(std::make_pair(posNow.first, posNow.second));
		if (it == mapCloseList.end())
			break;
		if (it->first.first == -1 && it->first.second == -1)
			break;
		cout << it->first.first << " " << it->first.second << " -> ";
		posNow = std::make_pair(it->second.iFPosx, it->second.iFPosy);
	}
}
int main()
{
	int **pstAr = new int *[5];
	for (int i = 0; i < 5; ++i)
		pstAr[i] = new int[5];
	
	for (int i = 0; i < 5; ++i)
		for (int j = 0; j < 5; ++j)
			pstAr[i][j] = 0;

	//pstAr[0][2] = 1;
	pstAr[1][2] = 1;
	pstAr[2][2] = 1;
	pstAr[3][2] = 1;
	//pstAr[4][2] = 1;
	pstAr[1][1] = 1;
	pstAr[3][0] = 1;
	pstAr[4][0] = 1;
	findRoad(pstAr, std::pair(1,0), std::pair(4,4));
	//findRoad(pstAr, std::pair(1, 0), std::pair(0, 3));
	return 0;
}

以上这份代码里的屌丝,最终找到了女朋友。

A*寻路算法_第2张图片

 

        昨天是A*寻路算法发明人Nilsson去世的日子,谨以此文致敬。

你可能感兴趣的:(高级数据结构和算法)