由于最短路径需要比较每一条可能的路径,所以基本上要遍历所有的节点,由此产生的开销实在是太大。于是我们就有了A* 算法这种在大方向上朝目标前进,在小范围内求最优路径的算法。A* 算法并不是找出最优路径,而是找一个相对来说比较理想的路径。
Astar.h
#pragma once
#include
#include
#define COST 10
using namespace std;
struct Point {
int x;
int y;
int G, H, F;
Point* parent;
};
list Path( Point* start, Point* end);
Point* newPoint(const int x,const int y);//申请新节点
void initAstar(int* maze,const int row,const int col);
void clearAstar();
Astar.cpp
#include
#include "Astar.h"
static int* Maze;//迷宫对应的二维数组
static int row;//行
static int col;//列
static list openList;//可能要走的路径
static list closeList;//已经走过的路径
static Point* getPath( Point* start, Point* end);
static Point* isInlist( list& L, Point* P);//判断坐标是否在列表中
static Point* getLeast();//得到F值最小的点
static vector getSurrondPoint( Point* p);//得到附近可用的坐标
static bool isCanReach(Point* p, Point* target);//节点是否能到达
static int getG(Point* p);
static int getH(Point* p, Point* end);
static int getF(Point* p);
void initAstar(int* maze, const int r, const int l) {
Maze = maze;
row = r;
col = l;
}
Point* newPoint(const int x, const int y) {
Point* p = new Point;
memset(p, 0, sizeof(Point));
p->x = x;
p->y = y;
return p;
}
list Path(Point* start, Point* end) {
list L;
Point* result = getPath(start, end);
while (result) {
L.push_front(result);
result = result->parent;
}
return L;
}
void clearAstar() {
Maze = NULL;
row = col = 0;
list::const_iterator it = openList.begin();
while (it != openList.end()) {
delete* it;
it = openList.erase(it);
}
it = closeList.begin();
while (it != closeList.end()) {
delete* it;
it = closeList.erase(it);
}
}
static Point* getPath( Point* start, Point* end) {
openList.push_back(newPoint(start->x, start->y));//首先将起点放入可走的列表,注意内外隔离
vector surrondPoint;
Point* leastPoint = NULL;//F值最小的点
Point* exist = NULL;//是否存在于可行列表中
vector::iterator it = surrondPoint.begin();
while (!openList.empty()) {//如果可行列表中还有元素
leastPoint = getLeast();
openList.remove(leastPoint);//从可行列表移动到关闭列表
closeList.push_back(leastPoint);
surrondPoint = getSurrondPoint(leastPoint);//找到周围的可行坐标
it = surrondPoint.begin();
while (it != surrondPoint.end()) {
exist = isInlist(openList, *it);
if (exist ==NULL) {//如果不在可行列表中
(*it)->G = getG(*it);
(*it)->H = getH(*it,end);
(*it)->F = getF(*it);
(*it)->parent = leastPoint;
openList.push_back(*it);
}
else {//在可行列表中
if (exist->G > getG(*it)) {//原路径开销更大
exist->G = getG(*it);
exist->F = getF(exist);
exist->parent = leastPoint;//更新路径
}
delete *it;//没有存在列表的元素要自己负责析构
}
++it;
}
surrondPoint.clear();
Point* result = isInlist(openList, end);
if (result) {
return result;
}
}
return NULL;//没有找到路径
}
static Point* isInlist( list& L, Point* p) {
if (L.empty()) {
return NULL;
}
list::const_iterator it = L.begin();
while (it != L.end()) {
if ((*it)->x == p->x && (*it)->y == p->y) {
return *it;
}
++it;
}
return NULL;
}
static Point* getLeast() {
if (openList.empty()) {
return NULL;
}
list::iterator it = openList.begin();
Point* result = *it;
while (it != openList.end()) {
if ((*it)->F < result->F) {
result = *it;
}
++it;
}
return result;
}
static vectorgetSurrondPoint( Point* p) {
vectorv;
for (int i = p->x - 1; i <= p->x + 1; ++i) {
for (int j = p->y - 1; j <= p->y + 1; ++j) {
Point* target = newPoint(i, j);
if (isCanReach(p, target)) {//可以抵达
v.push_back(target);
}
else {
delete target;//不可以抵达的坐标要及时析构
}
}
}
return v;
}
static bool isCanReach(Point* p, Point* target) {
if (target->x<0 || target->x>row - 1 || target->y<0 || target->y>col - 1
|| Maze[target->x * col + target->y] == 1 || Maze[target->x * col + target->y] == 2
|| (target->x == p->x && target->y == p->y)|| isInlist(closeList, target)) {
return false;
}
if (abs(p->x - target->x + p->y - target->y) == 1) {
return true;
}
return false;
}
static int getG( Point* p) {
return p->parent ? p->parent->G + COST : COST;
}
static int getH(Point* p, Point* end) {
//直接求那条斜线的长度
return static_cast(sqrt(static_cast(end->x - p->x * end->x - p->x) +
static_cast(end->y - p->y * end->y - p->y)))*COST;
}
static int getF(Point* p) {
return p->G + p->H;
}
main.cpp
#include
#include
#include "Astar.h"
using namespace std;
int map[13][13] = {//二维数组在内存顺序存储的
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, },
{ 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, },
{ 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, },
{ 0, 1, 0, 1, 0, 1, 2, 1, 0, 1, 0, 1, 0, },
{ 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, },
{ 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, },
{ 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, },
{ 2, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 0, 2, },
{ 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, },
{ 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, },
{ 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, },
{ 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }
};
void AstarTest();
int main(void) {
AstarTest();
return 0;
}
void AstarTest() {
Point* start = newPoint(12, 4);
Point* end = newPoint(0, 0);
initAstar(&map[0][0], 13, 13);
listL = Path(start, end);
for (auto it = L.begin(); it != L.end(); ++it) {
printf("(%d,%d)\n", (*it)->x, (*it)->y);
//Sleep(1000);
}
clearAstar();//清理内存
delete start;
delete end;
}
我们一定要记住内存的释放,在我们这个算法中,多处程序都可能会造成内存泄漏,如果我们不再使用他,并且并没有将它交给其他接口管理,那我们一定要手动释放它。还有救是要注意内外的隔离,主函数内的内存要由主函数自己来析构,所以接口中应该要为自己分配新的内存。