- A*算法求解迷宫最短路问题(C++,VScode)
- 一、算法思想及实现思路
- 1.估价函数:
- 2.open表,closed表:
- 二、问题求解所用到的方法以及数据结构
- 1.优先队列
- 2.二维结构体数组
- 3.估价函数的设计
- (1)曼哈顿法:
- (2)欧氏距离:
- (3)对角化距离:
- 三、源码
- 四、运行结
- 五、写在最后:
开始正文之前,先介绍一篇对我帮助较大一篇博客: A*算法讲解 一、算法思想及实现思路 1.估价函数:
F = G + H,起点为S,终点为E,从S到E经过节点N,S->N所要耗费的代价为已知代价G,N->E所要耗费的实际代价未知,因为当前在节点N,还未走到终点,所以未知路径,代价也未知,但可以设计启发函数来估计N到E要耗费的代价H,从而得出S->N->E所要耗费的代价的估计值F = G + H。
2.open表,closed表:起初,将起点放到open表中,对open表初始化,因为可以在地图上斜着走,所以对节点扩展时,设当前正在扩展的节点为N,要沿八个方向按顺序依次访问N的相邻八个节点,设相邻八个节点的某个节点为M,访问N周围节点时会遇到以下三种情况:
(1)节点在之前从未被访问过,即节点既不在open表,也不在closed表,此时节点放到open表中,放入之前,应先计算M的F,G,H值,并把当前正在被扩展的节点N作为它的父节点,M的G值是由父节点的G值加上父节点到M的实际距离(二者相邻),再根据估价函数计算H值,再计算F值,根据F值将M放入到open表的正确位置,open表中排在前面的,估计代价F值就越小。
(2)节点在之前被访问过,但未被扩展,即节点在open表,但不在closed表,此时可能要对M的G,F值更新,H值不变,因为它在地图的位置没变,相对终点位置没变,因为M在之前被访问过,所以它已有父节点,但不是当前正在被扩展的节点N,此时,计算N->M要耗费的代价,再加上N的G值,即为S->N->M所要耗费的实际代价,比较新G值与旧G值,若新<旧,则更新M的节点信息,将N作为它的父节点,不在以之前的父节点继续作为当下的父节点,并将旧G值用新G值覆盖,重新计算F值,然后根据F值调整M在open表中的位置(往前调)。
(3)节点在之前已被扩展过,即节点不在open表,在closed表,此时可以直接忽略M,转到N的下一个相邻节点。
当找到终点,且终点被放到closed表中,即可结束搜索,根据子节点存储的父节点信息,往回走,便能得到最短路。
二、问题求解所用到的方法以及数据结构 1.优先队列根据open表的特性,优先队列正好能和它相匹配,优先队列里的每一个元素都是一个结构体变量,存储自己的行列,以及F,G,H值,当然,需要对运算符重载,保证入队的元素是根据F值进行排序的。
2.二维结构体数组(1)作用:用来存储每一个节点的信息:父节点的位置,及自身的F,G,H值;记录最短路径,终点扩展后,根据该数组,便可从终点返回至起点。
(2)使用原因:优先队列每次只能修改或读取队首这一个节点,其他节点无法修改,只能出队才能遍历,很不方便,不能反复地、灵活地对队列中的任意一个节点进行修改或读取,也正是如此,如果队列中地节点信息要更新(说明从其它路径到该节点的G值更小),则直接创建一个新的节点,新旧节点均指向地图上的同一位置,即,某一位置的节点信息已经在队列中后,若要对节点信息修改,由于队列只能取队首,不能取到其他节点,所以,最好的做法便是重新为该位置创建新节点,放到队列中,新节点的F值比旧节点的F值更小,故而拍在队列的前面,排在前面的优先扩展,扩展后的节点所对应的位置,该位置的其他在队列中的节点不在扩展,直接跳过,因为那已经没有利用价值了。
3.估价函数的设计 (1)曼哈顿法:节点到终点的行之差的绝对值+列之差的绝对值:
∣
r
1
−
e
n
d
r
∣
+
∣
c
1
−
e
n
d
c
∣
|r1-end_r| + |c1-end_c|
∣r1−endr∣+∣c1−endc∣
两点之间直线距离:
s
q
r
t
(
p
o
w
(
(
r
1
−
e
n
d
r
)
,
2
)
+
p
o
w
(
(
c
1
−
e
n
d
c
)
,
2
)
)
sqrt(pow((r1-end_r),2) + pow((c1-end_c),2))
sqrt(pow((r1−endr),2)+pow((c1−endc),2))
以节点与终点的为对角线构成的矩形,选取最大正方形(顶点含节点),取其对角线,再从节点相对的点出发,横/竖着走到终点:
三、源码1.map数组:用于将结果打印,实现可视化(C++可视化粗糙,暂时未去寻找更好的可视化方法)。
2.q数组:二维结构体数组,作用上面已述。
3.heap:优先队列,实现open表,功能已述。
4.index数组:存储8个方向的增量
5.visited数组:标志位数组,=0,为遍历;=1,已遍历,但未扩展;=2,已扩展;
6.FindWay函数:A*算法主要实现函数
创建障碍物这部分比较简陋,不是算法核心,随便设置了一些。
#include#include #include "iostream" #include #include using namespace std; struct Node{ int row,col; int f,g,h; Node(int r,int c,int ff,int gg,int hh):row(r),col(c),f(ff),g(gg),h(hh){} //push(int r,int c,int ff,int gg,int hh):row(r),col(c),f(ff),g(gg),h(hh){} }; typedef struct info{ int row,col; int f,g,h; }Info; struct cmp{ bool operator()(Node a,Node b){ return a.f > b.f; } }; typedef struct Ind{ int r,c; }Index; #define n 90 #define m 100 priority_queue ,cmp> heap; void create_Maze(char map[][m+2], int visited[][m+2], int start_r, int start_c, int end_r, int end_c) { // memset(map, 0, sizeof map); // memset(visited, 0, sizeof visited); for(int i=1,j=1;i<=n;i++) { j=1; for(;j<=m;j++) { visited[i][j] = 0; map[i][j] = ':'; } } for(int j=0;j abs((heap.top().col+index[i].c)-end_c)?(4*abs((heap.top().col+index[i].c)-end_c)+10*abs((heap.top().row+index[i].r)-end_r)):(4*abs((heap.top().row+index[i].r)-end_r)+10*abs((heap.top().col+index[i].c)-end_c)));//对角化距离 f = g + h; heap.push(Node(heap.top().row+index[i].r, heap.top().col+index[i].c, f, g, h));//子节点入队 //q[heap.top().row+index[i].r][heap.top().col+index[i].c].push(); //将子节点的信息保存到二维结构体数组q中 q[heap.top().row+index[i].r][heap.top().col+index[i].c].row = heap.top().row; q[heap.top().row+index[i].r][heap.top().col+index[i].c].col = heap.top().col; q[heap.top().row+index[i].r][heap.top().col+index[i].c].f = f; q[heap.top().row+index[i].r][heap.top().col+index[i].c].g = g; q[heap.top().row+index[i].r][heap.top().col+index[i].c].h = h; //if (i==5) cout<>n>>m; // int start_r,start_c;//起点 int end_r,end_c;//终点位置 cout<<"输入起点位置:"; cin>>start_r>>start_c; cout<<"输入终点位置:"; cin>>end_r>>end_c; // int c1 = abs(start_r - end_r)*10;//曼哈顿法 // int c2 = abs(start_c - end_c)*10; //int h = 10*sqrt((start_r - end_r)*(start_r - end_r) + (start_c - end_c)*(start_c - end_c));//两点间直线距离 int h = abs(start_r - end_r)==abs(start_c - end_c)?(abs(start_c - end_c)*14):(abs(start_r - end_r)>abs(start_c - end_c)?(4*abs(start_c - end_c)+10*abs(start_r - end_r)):(4*abs(start_r - end_r)+10*abs(start_c - end_c)));//对角化距离 //int h = c1 + c2; heap.push(Node(start_r,start_c,h,0,h)); Index index[8]; char map[n+2][m+2]; int visited[n+2][m+2];//p_row[n+2][m+2],p_col[n+2][m+2]; Info q[n+2][m+2]; q[start_r][start_c].row = -1; q[start_r][start_c].col = -1; q[start_r][start_c].f = h; q[start_r][start_c].g = 0; q[start_r][start_c].h = h; // create_Maze(map,visited,start_r,start_c,end_r,end_c); create_Ind(index); //cout< 四、运行结
图中S是起点,E是终点。
:是未遍历的点,#是墙,*是路径,=遍历过的点,也是搜索空间。
路径也有打印,只是篇幅太长,没有截图。
五、写在最后:发表一篇博客,以来记录自己的大二下课设。
整篇代码比较简陋,为了能实现A*算法,没有考虑太多的时空复杂度,用树来实现或许会更好,但限于本人水平有限,用的是数组。



