A*蒜法——C语言实现

概述

其实A*算法用来尝试解决的问题很简单,就是从源点移动到目的地,所花费的代价最小,找到这样一条路径,这个代价可以是时间,距离,金钱等。我们找路径时,关注的重点不再只是,找他去终点的最短路径,而是要综合考虑,路径长度和路径算法两个方面,简单的讲就是路径排序的依据不仅仅是路径长度,而是依据 L + P
这里,
L = 源点到终点的移动成本,也就是最短路径长度。
P = 该点到终点的预估成本,也就是试探,为什么说是试探呢,因为这个P只是猜测,真正的距离只有我们真的去遍历之后才会知道,本文会介绍两种计算H的方法,当然你也可以去谷歌找到更多的方法。

启发式函数

P的求取,重点是选区优秀的启发式函数,启发式函数h,告诉了我们从任意结点n到目标结点的最小代价评估值,选择一个优秀的启发函数是A*算法的关键。
那么一般会用的启发式函数是什么呢?

  1. 曼哈顿距离
    对于一般的网格来讲,曼哈顿距离是一个十分合适并且简单的启发函数,所谓曼哈顿距离,就是计算两点距离时,如果用一个直角三角形ABC, C为直角顶点来表示的话,欧几里得距离就是直角边c,曼哈顿距离就是两直角边之和(a + b),计算起来就仅仅是横坐标差的绝对值与纵坐标差的绝对值相加。

  2. 目标点到终点的最短路径长度
    如果我们不在网格而是在图中,或者曼哈顿距离的准确度不够,我们可以试试用最短路径长度做启发式函数,也就是说我们要提前用一个函数计算出任意两顶点之间的最短路径,(不难发现这会浪费很多的时间),我们可以用SPFA,Floyd等算法。

这里我们也不难发现,如果启发式函数h = 0,那么A*算法也就退化成了简单的Dijkstra算法,所以说重点就是在启发式函数的选取,至于操作部分就和Dijkstra区别不大了。

操作

(1)初始化:S中只包含源点u,U中包含除源点外的所有点,dist中与u相连的边如<u,v>,dist[v]设置为<u,v>的权值 (dist[u] = 0, 其余未连接的顶点的dist设置为INF,即无穷大),path[u] = v(其它未连接的点path均设置为-1)。
(2)从S中选择一个顶点s,U中选择一个顶点p,使得<s,p>是最小的,然后将U中的p加入到S中。
(3)更新S中的点的最短距离,即更新dist, path数组,(注意dist,数组中记录的最短路径长度和path数组中记录的最短路径都是当前条件下的最短路径,并非全局的最短路径,随着U中新顶点的不断加入,不断更新dist,path中的最短路径,当遍历完图中所有顶点,得到的就是源点到其它顶点的最短路径)。
更新方法:因为<s,u>是我们找到的最短的那条边,所以我们只需要判断(<u,v>的权值 + h(u))与(<u,p> + h(u)) + (<p,v> + hp)的权值哪个更小,若新加入的p使得源点u到顶点v的距离缩短了,即(<u,p> + h(u) + <p,v> + hp) < (<u,v> + h(u)),那么我们就更新dist,path中关于v的数据。
(4)循环重复(2),(3)的操作,直到U中所有的顶点都加入到S中。

所以其实A*操作起来很简单,就是把Dijkstra里面的判断标准dist改为(dist + h)即可,读者可参照另一篇Dijkstra的博客
这里的代码则是基于BFS的一种思路。

代码实现

数据结构

typedef struct node {
	int data;
	int  g, h;
	int parent;
}node;
typedef struct {
	int data;
	int parent;
}QUERE1;              //队列的数据结构
typedef struct ANode {
	int adjvex;
	struct ANode *nextarc;
	int weight;
}ArcNode;             //边结点的类型
typedef struct Vnode {
	int info;
	ArcNode *firstarc;
}VNode;
typedef struct {
	VNode adjlist[MAXV];
	int n, e;
}AdjGraph;

算法实现

void Astar(AdjGraph G, int start, int end) {
	int i, w;

	int path1[MAXV];
	int count = 0;
	ArcNode *p;
	node qu[MAXV];                 //基于BFS使用的队列
	int front = -1; int rear = -1;
	int visited_Astar[MAXV] = { 0 };
	rear++;
	qu[rear].data = start;
	qu[rear].parent = -1;
	visited_Astar[start] = 1;
	while (front != rear) {
		front++;
		w = qu[front].data;
		if (w == end) {
			i = front;
			while (qu[i].parent != -1)
			{
				path1[count] = qu[i].data;
				count++;
				i = qu[i].parent;
			}
			path1[count] = qu[i].data;
			int m = count;
			for (int k = 0; k < m; k++) {
				path_Astar[k] = path1[count];
				count--;
			}
			path_Astar[m] = end;
			return;
		}

		p = G.adjlist[w].firstarc;
		int minF = qu[p->adjvex].g + qu[p->adjvex].h;
		while (p != NULL) {
			if (visited_Astar[p->adjvex] == 0) {
				visited_Astar[p->adjvex] = 1;
				rear++;
				qu[rear].data = p->adjvex;
				qu[rear].g = 0;
				qu[rear].g += p->weight;
				qu[rear].parent = front;
				qu[rear].h = SPFA(G, qu[rear].data, end);
			}
			p = p->nextarc;
		}
		for (int i = front + 1; i <= rear - 1; i++) {
			if (qu[i].g + qu[i].h > qu[i + 1].g + qu[i + 1].h) {
				node tmp = qu[i];
				qu[i] = qu[i + 1];
				qu[i + 1] = tmp;
			}
		}
	}
}

关于代码中使用的启发式函数h,这里采用的SPFA算法,如果想使用曼哈顿距离或其它,只需修改代码中的spfa为h,h为自己编写的启发式函数。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值