A*算法求解迷宫最短路问题(C++,VScode)

A*算法求解迷宫最短路问题(C++,VScode)

文章目录

  • 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| r1endr+c1endc
请添加图片描述

(2)欧氏距离:

两点之间直线距离:
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((r1endr),2)+pow((c1endc),2))

请添加图片描述

(3)对角化距离:

以节点与终点的为对角线构成的矩形,选取最大正方形(顶点含节点),取其对角线,再从节点相对的点出发,横/竖着走到终点:

请添加图片描述

三、源码

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;jabs((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<

四、运行结

请添加图片描述
A*算法求解迷宫最短路问题(C++,VScode)_第1张图片

图中S是起点,E是终点。

:是未遍历的点,#是墙,*是路径,=遍历过的点,也是搜索空间。

路径也有打印,只是篇幅太长,没有截图。

五、写在最后:

发表一篇博客,以来记录自己的大二下课设。

整篇代码比较简陋,为了能实现A*算法,没有考虑太多的时空复杂度,用树来实现或许会更好,但限于本人水平有限,用的是数组。

你可能感兴趣的:(算法,数据结构,c++)