A* 算法实现与解析

【算法分析】
A*算法适用于
结点特别多的场景,常借助于优先队列 priority_queue 实现。
在A*算法的实现中,形如下方的代码,为“
结构体内嵌比较函数”。之所以这样写,是因为其执行速度快。
bool operator<(const NODE &a) const {
    return F==a.F?G>a.G:F>a.F;
}


下面算法代码执行后,将输出一条如下图所示的绿色最短路径。


 

A*算法的经典解析可参见:
https://cloud.tencent.com/developer/article/2040931
https://blog.csdn.net/m0_46304383/article/details/113457800
https://blog.csdn.net/dujuancao11/article/details/109749219
https://www.acwing.com/blog/content/352/
https://blog.csdn.net/qq_43827595/article/details/99546055


【算法代码】
下面代码改编自:https://blog.csdn.net/m0_46304383/article/details/113457800

#include <bits/stdc++.h>
using namespace std;

const int N=10; //Map order
bool close[N][N]; //Access records, close lists
int valueF[N][N]; //Record the F value of each node
int pre[N][N][2]; //Stores the parent node of each node

int env[N][N]= { //Scene Map
	{0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
	{0, 1, 0, 0, 0, 0, 0, 1, 0, 0},
	{0, 0, 1, 1, 0, 0, 0, 1, 0, 0},
	{0, 0, 0, 1, 0, 0, 0, 1, 0, 0},
	{0, 0, 0, 0, 0, 0, 1, 0, 0, 0},
	{0, 0, 1, 1, 0, 1, 0, 0, 0, 0},
	{0, 0, 1, 0, 1, 0, 1, 0, 0, 0},
	{0, 0, 1, 0, 0, 0, 0, 1, 0, 0},
	{0, 1, 0, 1, 0, 0, 0, 0, 0, 0},
	{0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
};

struct NODE {
	int x,y;   //Node location
	int F,G,H; //F=G+H
	NODE(int a, int b) { //constructor function
		x=a,y=b;
	}
	//struct's nested comparison function,speed is faster.
	bool operator<(const NODE &a) const {
		return F==a.F?G>a.G:F>a.F;
	}
};

//define the direction
//const int ne_pos[8][2]={{-1,-1},{-1,0},{-1,1},{0,1},{1,1},{1,0},{1,-1},{0,-1}};
const int ne_pos[4][2]= {{-1,0},{0,1},{1,0},{0,-1}};
priority_queue<NODE> open;

int Manhattan(int x0,int y0,int x1,int y1) {
	return (abs(x0-x1)+abs(y0-y1));
}

bool isValidNode(int x,int y,int u,int v) {
	if(x<0||x>=N||y<0||y>=N) return false; //Judge boundary
	if(env[x][y]==1) return false; //Judge obstacle
	/*When two nodes are diagonal and
	their common adjacent nodes have obstacles, return false.
	The rule is used when the scene map has 8 directions.*/
	if(x!=u && y!=v && (env[x][v]==1 || env[u][y]==1)) return false;
	return true;
}

void Astar(int x0,int y0,int x1,int y1) {
	NODE node(x0,y0); //Starting point is added into the open list.
	node.G=0;
	node.H=Manhattan(x0,y0,x1,y1);
	node.F=node.G+node.H;
	valueF[x0][y0]=node.F;
	open.push(node);

	while(!open.empty()) {
		/*Take the head element of priority queue,
		which is the point with the least cost in the surrounding cells*/
		NODE cur_node=open.top();
		open.pop(); //Remove from the open list
		//Access this point and add it to the close list
		close[cur_node.x][cur_node.y]=true;
		if(cur_node.x==x1 && cur_node.y==y1) break; //Reach the finish line

		/*Traverse the four directions around the top_node,
		or if the array ne_pos has eight values,
		then you need to traverse the eight directions around the top_node.
		*/
		for(int i=0; i<4; i++) {
			//Create a point around top_node
			NODE ne_node(cur_node.x+ne_pos[i][0],cur_node.y+ne_pos[i][1]);
			//The node coordinate is valid and has not been accessed
			if(isValidNode(ne_node.x,ne_node.y,cur_node.x,cur_node.y) && !close[ne_node.x][ne_node.y]) {
				/*Calculate the cost to reach the node
				from the starting point and through the top_node node*/
				ne_node.G=cur_node.G+int(sqrt(pow(ne_pos[i][0],2)+pow(ne_pos[i][1],2)));
				//Calculate the Manhattan distance from the node to the destination
				ne_node.H=Manhattan(ne_node.x,ne_node.y,x1,y1);
				/*The estimated cost to reach the destination
				 from the starting point through the top_node
				 and the current node*/
				ne_node.F=ne_node.G+ne_node.H;

				/*ne_node.F<valueF[ne_node.x][ne_node.y],
				  When the condition is met, a better path is found and updated.
				  valueF[ne_node.x][ne_node.y]==0,
				  When the condition is met, the node is not added to the OPEN table,
				  it is needed to be added to the open table*/
				if(ne_node.F<valueF[ne_node.x][ne_node.y] || valueF[ne_node.x][ne_node.y]==0) {
					//Save the parent node of the node
					pre[ne_node.x][ne_node.y][0]=cur_node.x;
					pre[ne_node.x][ne_node.y][1]=cur_node.y;
					//Modify the valueF value of the node
					valueF[ne_node.x][ne_node.y]=ne_node.F;
					open.push(ne_node);
				}
			}
		}
	}
}

void PrintPath(int x1,int y1) {
	if(pre[x1][y1][0]==-1 || pre[x1][y1][1]==-1) {
		printf("No path to get.\n");
		return;
	}
	int x=x1,y=y1;
	int a,b;
	while(x!=-1 || y!=-1) {
		env[x][y]=2; //Assign a value of 2 to the node on the feasible path
		a=pre[x][y][0];
		b=pre[x][y][1];
		x=a;
		y=b;
	}

	/*
	"  " represents an unpassed node,
	" #" represents an obstacle,
	" @" represents a feasible node.
	*/
	string s[3]= {"  "," #", " @"};
	for(int i=0; i<N; i++) {
		for(int j=0; j<N; j++) {
			cout<<s[env[i][j]]; //s[] is string, must use cout to output
		}
		cout<<endl;
	}
}

int main(){
	memset(close,false,sizeof close); //Initialize close array to false
	memset(valueF,0,sizeof valueF); //Initialize F to 0
	memset(pre,-1,sizeof pre); //Initialize pre array to -1

	int x0=2,y0=4; //start point
	int x1=8,y1=6; //end point

	if(!isValidNode(x0,y0,x0,y0)){
		printf("Invalid input.\n");
		return 0;
	}

	Astar(x0,y0,x1,y1); //A* algorithm
	PrintPath(x1,y1); //Print the path

	return 0;
}


/*
output:
             @ @ @
   #         @ # @
     # # @ @ @ # @
       #       # @
             #   @
     # #   #     @
     #   #   #   @
     #         # @
   #   #     @ @ @
*/


【未加注释的算法代码】
鉴于上面代码注释过多,为方便大家快速阅读代码,下面给出的是未加注释版本的算法代码。

#include <bits/stdc++.h>
using namespace std;

const int N=10;
bool close[N][N];
int valueF[N][N];
int pre[N][N][2];

int env[N][N]= {
	{0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
	{0, 1, 0, 0, 0, 0, 0, 1, 0, 0},
	{0, 0, 1, 1, 0, 0, 0, 1, 0, 0},
	{0, 0, 0, 1, 0, 0, 0, 1, 0, 0},
	{0, 0, 0, 0, 0, 0, 1, 0, 0, 0},
	{0, 0, 1, 1, 0, 1, 0, 0, 0, 0},
	{0, 0, 1, 0, 1, 0, 1, 0, 0, 0},
	{0, 0, 1, 0, 0, 0, 0, 1, 0, 0},
	{0, 1, 0, 1, 0, 0, 0, 0, 0, 0},
	{0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
};

struct NODE {
	int x,y;
	int F,G,H;
	NODE(int a, int b) {
		x=a,y=b;
	}
	bool operator<(const NODE &a) const {
		return F==a.F?G>a.G:F>a.F;
	}
};

const int ne_pos[4][2]= {{-1,0},{0,1},{1,0},{0,-1}};
priority_queue<NODE> open;

int Manhattan(int x0,int y0,int x1,int y1) {
	return (abs(x0-x1)+abs(y0-y1));
}

bool isValidNode(int x,int y,int u,int v) {
	if(x<0||x>=N||y<0||y>=N) return false;
	if(env[x][y]==1) return false;
	if(x!=u && y!=v && (env[x][v]==1 || env[u][y]==1)) return false;
	return true;
}

void Astar(int x0,int y0,int x1,int y1) {
	NODE node(x0,y0);
	node.G=0;
	node.H=Manhattan(x0,y0,x1,y1);
	node.F=node.G+node.H;
	valueF[x0][y0]=node.F;
	open.push(node);

	while(!open.empty()) {
		NODE cur_node=open.top();
		open.pop();
		close[cur_node.x][cur_node.y]=true;
		if(cur_node.x==x1 && cur_node.y==y1) break;

		for(int i=0; i<4; i++) {
			NODE ne_node(cur_node.x+ne_pos[i][0],cur_node.y+ne_pos[i][1]);
			if(isValidNode(ne_node.x,ne_node.y,cur_node.x,cur_node.y) && !close[ne_node.x][ne_node.y]) {
				ne_node.G=cur_node.G+int(sqrt(pow(ne_pos[i][0],2)+pow(ne_pos[i][1],2)));
				ne_node.H=Manhattan(ne_node.x,ne_node.y,x1,y1);
				ne_node.F=ne_node.G+ne_node.H;
				if(ne_node.F<valueF[ne_node.x][ne_node.y] || valueF[ne_node.x][ne_node.y]==0) {
					pre[ne_node.x][ne_node.y][0]=cur_node.x;
					pre[ne_node.x][ne_node.y][1]=cur_node.y;
					valueF[ne_node.x][ne_node.y]=ne_node.F;
					open.push(ne_node);
				}
			}
		}
	}
}

void PrintPath(int x1,int y1) {
	if(pre[x1][y1][0]==-1 || pre[x1][y1][1]==-1) {
		printf("No path to get.\n");
		return;
	}
	int x=x1,y=y1;
	int a,b;
	while(x!=-1 || y!=-1) {
		env[x][y]=2;
		a=pre[x][y][0];
		b=pre[x][y][1];
		x=a;
		y=b;
	}

	string s[3]= {"  "," #", " @"};
	for(int i=0; i<N; i++) {
		for(int j=0; j<N; j++) {
			cout<<s[env[i][j]];
		}
		cout<<endl;
	}
}

int main() {
	memset(close,false,sizeof close);
	memset(valueF,0,sizeof valueF);
	memset(pre,-1,sizeof pre);

	int x0=2,y0=4;
	int x1=8,y1=6;
	if(!isValidNode(x0,y0,x0,y0)) {
		printf("Invalid input.\n");
		return 0;
	}

	Astar(x0,y0,x1,y1);
	PrintPath(x1,y1);

	return 0;
}


/*
output:
             @ @ @
   #         @ # @
     # # @ @ @ # @
       #       # @
             #   @
     # #   #     @
     #   #   #   @
     #         # @
   #   #     @ @ @
*/



【参考文献】
https://cloud.tencent.com/developer/article/2040931
https://blog.csdn.net/m0_46304383/article/details/113457800
https://blog.csdn.net/dujuancao11/article/details/109749219
https://www.acwing.com/blog/content/352/
https://blog.csdn.net/qq_43827595/article/details/99546055

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值