前几天跟着这个教程学了A星算法,发现如果遇到规模大且复杂的地图,识别速度会非常慢,个人认为是一个格子会被周围八个点进行反复搜索导致的,因此改进了算法,识别效率大幅提升,还做了个可视化,大家可以参考
#include<vector>
#include <iostream>
#include<cstring>
#include<stdlib.h>
#include<Windows.h>
#define ROWS 20 //二十行二十列
#define COLS 20
#define ZXDJ 10 //直线和斜线代价
#define XXDJ 14
enum dirent { p_up, p_dowm, p_left, p_right, p_lup, p_rup, p_ldowm, p_rdowm }; //枚举
using namespace std;
//定义地图上的点
struct point
{
int x=0, y=0; //数组下标,表示地图中位置
int f=0, g=0, h=0; //量化评估
};
struct treeNode { //树的节点
point pos;
vector<treeNode*> child; //父有多子
treeNode* pParent = NULL; //子有一父
};
//创建树节点的函数
treeNode* createTreeNode(int x, int y)
{
treeNode* pNew = new treeNode;
treeNode tlist = { 0 }; //初始化,将内存全部赋值为0,即NULL
pNew->pos.x = x;
pNew->pos.y = y;
return pNew;
}
void FindAroundPoint(int i, treeNode* pChild)
{
switch (i)
{
case p_up:
if (pChild->pos.x == 0)
{
break;
}
pChild->pos.x--; //如果往上走,x-1
pChild->pos.g += ZXDJ;
break;
case p_dowm:
if (pChild->pos.x == 19)
{
break;
}
pChild->pos.x++; //如果往下走,x+1
pChild->pos.g += ZXDJ;
break;
case p_left:
if (pChild->pos.y == 0)
{
break;
}
pChild->pos.y--; //如果往左走,y-1
pChild->pos.g += ZXDJ;
break;
case p_right:
if (pChild->pos.y == 19)
{
break;
}
pChild->pos.y++; //如果往右走,y+1
pChild->pos.g += ZXDJ;
break;
case p_lup:
if (pChild->pos.x == 0 ||pChild->pos.y == 0)
{
break;
}
pChild->pos.x--; //如果往左上走,x-1,y-1
pChild->pos.y--;
pChild->pos.g += XXDJ;
break;
case p_rup:
if (pChild->pos.x == 0 || pChild->pos.y == 19)
{
break;
}
pChild->pos.x--; //如果往右上走,x-1,y+1
pChild->pos.y++;
pChild->pos.g += XXDJ;
break;
case p_ldowm:
if (pChild->pos.x == 19 || pChild->pos.y == 0)
{
break;
}
pChild->pos.x++; //如果往左下走,x+1,y-1
pChild->pos.y--;
pChild->pos.g += XXDJ;
break;
case p_rdowm:
if (pChild->pos.x == 19 || pChild->pos.y == 19)
{
break;
}
pChild->pos.x++; //如果往右下走,x+1,y+1
pChild->pos.y++;
pChild->pos.g += XXDJ;
break;
}
}
//计算当前点与终点的曼哈顿距离
int getH(point pos, point endPos)
{
int x, y;
if (endPos.x > pos.x)
{ x = endPos.x - pos.x; }
else{ x = pos.x - endPos.x; }
if (endPos.y > pos.y)
{ y = endPos.y - pos.y; }
else { y = pos.y - endPos.y; }
return ZXDJ * (x + y);
}
void print3(int map[ROWS][COLS], int row, int col, point begin, point end ,treeNode *pCurrent) //在cmd中输出路径
{
int i = 0;
int j = 0;
for (i = 0; i < row; i++)
{
for (j = 0; j < col; j++)
{
// cmd该颜色句柄
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
// 获取当前文本属性
CONSOLE_SCREEN_BUFFER_INFO consoleInfo;
GetConsoleScreenBufferInfo(hConsole, &consoleInfo);
WORD originalAttributes = consoleInfo.wAttributes;
if (map[i][j] == 1) //墙
{
SetConsoleTextAttribute(hConsole,BACKGROUND_BLUE | FOREGROUND_INTENSITY); //蓝墙
}
if (map[i][j] == 2) //搜索过的点
{
// 设置新的文本属性,可以选择以下颜色组合
// FOREGROUND_RED, FOREGROUND_GREEN, FOREGROUND_BLUE,
// FOREGROUND_INTENSITY, BACKGROUND_RED, BACKGROUND_GREEN,
// BACKGROUND_BLUE, BACKGROUND_INTENSITY
SetConsoleTextAttribute(hConsole, FOREGROUND_RED | FOREGROUND_INTENSITY); //红字
}
if (map[i][j] == 3) //最终路径
{
SetConsoleTextAttribute(hConsole, FOREGROUND_GREEN | FOREGROUND_INTENSITY); //绿字
}
if ((begin.x == i && begin.y == j) || (end.x == i && end.y == j)) //起点终点
{
SetConsoleTextAttribute(hConsole, BACKGROUND_GREEN | BACKGROUND_INTENSITY); //浅蓝墙
}
printf("%d ", map[i][j]);
// 恢复原始文本属性
SetConsoleTextAttribute(hConsole, originalAttributes);
}
printf("\n");
}
}
void IsFindEnd(bool isFindEnd, int map[ROWS][COLS], point begPos, point endPos, treeNode* pCurrent)
{
if (isFindEnd)
{
printf("找到终点\n");
printf("路径");
while (pCurrent) //打印路径的点
{
printf("(%d,%d)", pCurrent->pos.x, pCurrent->pos.y);
map[pCurrent->pos.x][pCurrent->pos.y] = 3; //最终路径会在可视化时显示3
pCurrent = pCurrent->pParent; //往上走,子走到父
}
printf("\n");
}
else
{
printf("未找到终点\n");
}
print3(map, ROWS, COLS, begPos, endPos, pCurrent); //可视化
}
int main()
{
int map[ROWS][COLS] = {
{ 0,0,1,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0 },
{ 1,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0 },
{ 1,0,0,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0 },
{ 1,0,1,1,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0 },
{ 1,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0 }, //4
{ 0,0,1,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,0,0,0,0,0,0,0,0,0 },
{ 0,0,1,1,0,0,0,0,0,0,1,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 }, //8
{ 0,1,1,1,1,1,1,1,0,0,1,0,0,1,1,1,1,1,0,0 },
{ 0,0,0,0,0,1,0,1,0,0,1,0,0,0,0,0,0,1,0,0 },
{ 0,0,0,0,0,1,0,1,0,0,1,1,1,1,1,1,0,1,1,1 },
{ 1,0,0,0,0,1,0,1,0,0,1,0,0,0,0,1,0,0,0,0 }, //12
{ 1,0,0,0,0,1,0,1,1,1,1,0,0,0,0,1,0,0,0,0 },
{ 1,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,0,0 },
{ 1,0,0,0,1,1,1,1,0,0,1,0,0,0,0,0,0,0,0,0 },
{ 1,1,1,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0 }, //16
{ 1,0,0,0,0,1,0,1,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,0,0,0,0 },
{ 1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0 },
};
point begPos = { 0,0 };
point endPos = { 8,8 };
bool pathMap[ROWS][COLS] = { 0 }; //走过的地图,虚拟地图,全false,走过标记1(true)
// 准备一棵树,从起点开始,循环寻路,找出周围能走的点,入树,入容器,
// 挑出数组中f最小的点,走,遇终点或没终点:停,显示路径
treeNode* pRoot = createTreeNode(begPos.x, begPos.y); //起点是树的根节点
pathMap[begPos.x][begPos.y] = true; //虚拟地图里标记起点已经走过
treeNode* pCurrent = pRoot; //当前点
treeNode* pChild = NULL; //根节点的孩子
treeNode* tmp3 = NULL; //临时节点
//准备一个数组,用来存周围一圈的点
vector<treeNode*>buff;
vector<treeNode*>::iterator it; //变化的迭代器
vector<treeNode*>::iterator itMin; //记录最小的
vector<treeNode*>::iterator tmp2;
bool isFindEnd = false;
int o = 0; //用来打印走了几次
int k = 0; //用来判断搜索到的点是否在buff存在,是否需要计算
while (1)
{
//找出周围能走的点8个
for (int i = 0; i < 8; i++)
{
pChild = createTreeNode(pCurrent->pos.x, pCurrent->pos.y); //创建出当前点的孩子
FindAroundPoint(i, pChild); //找到周围八个点Pchild
//检查pChild是否能走 ( 是否是障碍物 或 已走过的点 或超过边界)
if (map[pChild->pos.x][pChild->pos.y] == 0 &&
pathMap[pChild->pos.x][pChild->pos.y] == false)
{
if (k != 0) //第一次第一个搜索不拦截
{
tmp2 = buff.begin();
for (; tmp2 != buff.end(); tmp2++)
{
tmp3 = *tmp2;
if (pChild->pos.x == tmp3->pos.x && pChild->pos.y == tmp3->pos.y) //如果搜索的,在buff里已经存在了,拦下来
{
k = 2;
delete pChild;
}
}
}
if (k != 2)
{
//计算h值
pChild->pos.h = getH(pChild->pos, endPos);
//计算f值
pChild->pos.f = pChild->pos.g + pChild->pos.h;
//入树
pCurrent->child.push_back(pChild); //老节点把新节点包括在内,新节点成为老节点孩子
pChild->pParent = pCurrent; //老节点成为新节点的父亲
//入容器
buff.push_back(pChild); //可走的路都进来了,然后再选取最小,走了就把最小的删去
}
k = 1;
}
else
{
delete pChild; //不能走,释放pChild
}
}
int buffSize = buff.size();
printf("buffSize = %d ", buffSize);
//数组中挑出 f 最小的点
//假设a最小,循环让b、c、d、e来跟a比较,如果c最小,让itMin等于c
it = buff.begin();
itMin = buff.begin();
for (; it != buff.end(); it++)
{
itMin = ((*itMin)->pos.f < (*it)->pos.f) ? itMin : it;
}
//走
pCurrent = *itMin;
pathMap[pCurrent->pos.x][pCurrent->pos.y] = true; //标记走过的点
map[pCurrent->pos.x][pCurrent->pos.y] = 2; //走过的路=2
printf("\n");
print3(map, ROWS, COLS, begPos, endPos, pCurrent); //可视化
printf("走了%d次\n", o);
o++;
Sleep(50);
system("cls");
//遇到终点或走完地图没终点,结束
if (endPos.x == pCurrent->pos.x && endPos.y == pCurrent->pos.y)
{
isFindEnd = true;
break;
}
if (buff.size() == 0)
{
break;
}
buff.erase(itMin); //删掉走过的点,不然会拿到下一次做比较,遇到死胡同会回路,先判断完再删掉,不然只有一个走了后删了,就没了
}
IsFindEnd(isFindEnd, map, begPos, endPos, pCurrent);
return 0;
}