1.简述
A星算法就是试图在地图中找到一条最短路径,但不保证一定存在。
- 任务
小猫去找青蛙玩(好TM弱智啊~) - 条件
黑色方块无法通行,每走一个格子小猫消耗的体力都为1。
2.如果说你是小猫,你会怎么走?
嗯,毫无疑问
你肯定这么走。。。很简单的对吧,但是你这么走的时候,真的没经过思考?并非如此吧,你要找最短路径是不是直接连线是最快的?但是连线有障碍物阻挡了,所以逼迫你先往上走出障碍物的区域,然后一路直线当然就是最短路径了。
BUT,你所做的思考,都是建立在你脑海里已经有整张地图的概念上,代码是不知道地图的,它要知道怎么走,就是一步一步的探索,你的行为对于代码来说,那叫-未卜先知!
3.基本概念
要实现A星算法,先要知道一些基本概念
- open列表
所谓open列表,我的理解是,知道但是没有走过的路 - close列表
已经走过的路 - F值
这个叫做和值,指的是走到终点消耗的代价值,这里就是指的是小猫消耗的体力了
F = G + H,所以要知道F值,需要计算G和H的值 - G值
从起点到该点消耗的代价 - H值
从该点到终点的预估代价,为何G值不是预估,这里是预估呢?因为G值是从起点到该点,是一段已经走过的路程,代价是准确可知的,H值是到终点,但是这段路还没有走,所以是一段预估的值。
这些概念听起来有辣么一点对角懵逼~,没关系,我们实际演示下
4.手工走起
我们用粉色代表open列表,用绿色代表close列表
首先起点是我们走过的第一个点,将它先加入到close列表中
然后这只猫有四个方向可以选择,分别是上下左右,将这四个方块加入到open表中,表示知道但是还没走过的路,现在是决定怎么走的时候了,于是我们分别求着四个方块的F值。
G值都是很好求的,都是1,从起点走到这里嘛,都只走一步,H值从该点到终点的预估代价,这个可怎么求?
一般来说,这个值就是忽略所有障碍的情况下走出来的值,很明显,在这种只能直行的条件下,H值就是两点之间的横向格子数差加上纵向格子数差了
上格子 H = 横向7 + 纵向2 = 9
下格子 H = 横向7 + 纵向0 = 7
左格子 H = 横向8 + 纵向1 = 9
右格子 H = 横向6 + 纵向1 = 7
加上G值可以求得他们的F值分别为10,8,10,8
将这些数据标注在格子上
上面是F值,下左是G,下右是H
有了这些数据,小猫就挑一个F值最小的走,这里8是最小,但是有两个,怎么办?无所谓了,随便走一条,但是一般是最后加入open表的那个格子。至于为什么无所谓,可以看完整篇文章反过来想想。
就走右边了,于是将右边的方块加入到close表,已经走过了嘛,记得要将它从open表中剔除
接着在这个新的格子上,又有4个选择的方向,但是这里要注意了,每次选择新格子加入open列表的时候,要保证3点
- 这个点不能在close表中
- 这个点不能在open表中
- 这个点是合法的,什么叫合法?这里简单的就是可以通行的,不能是障碍物
于是又加入了上下右三个点到open列表中,此时open列表已经有6个点了,接着计算这三个点的各项值
然后在open表中再次寻找F值最低的格子,发现是8,这里有3个,还是随意选一个,选下面的吧,将它从open表中剔除,加入close表中
紧接着再次寻找新的格子加入open列表中,依旧遵循上面说的3点,发现符合加入的只有右边的格子
接着还是计算新加入open列表的格子的值
在open列表中寻找F值最低的格子,发现还是8,有3个,我们选择最新加入的。
经过这几步之后,你是不是渐渐发现了规律?
5.伪代码
我们来总结下规律
将起点加入close表
while(结束条件)
{
获取close表的最后一个节点S
获取S点周围所有符合加入条件(条件就是指上文所说的那3点)的点,加入open列表
计算open列表F值最低的格子T
T从open表中删除加入close表
}
再次循环的时候,是不是第一步获取的节点S就是最后加入的T了,如此就可以跟随着这个T,一步步的扩展路径了
结束条件:如果有这条路径存在,则是当青蛙所在的节点被加入到close后,寻路就结束了。
或者不存在这条路径,那么就是open列表中不再有节点的时候。
open列表实际上是一步步往外扩展的格子,当这个列表没有节点的时候并且终点还没有在close列表中,
那么说明所有能扩展到的格子已经都被走过了,仍然没有走到终点,此时便是没有这条路了
6.注意项
A星得到的结果是一条路径,这条路径最终是反推回去的,所以在写Node的数据结构时候,要记录这个节点是从哪个节点走过来的,也就是他的父节点,如此在最终才能反推回去。
A星不是沿着一条路走到黑的,你会发现它是不停地计算open表中最小F值的格子,当最小F值超过10的时候,你发现它又开始从F为10的格子上开始探索,实际上是几条不同的路径轮询的探索,最终走向终点,这也是为为何在open列表F值最低同时存在多个的时候,随便选择的原因,因为一旦探索的格子F值超过了这个最低值,它又会回到这个最低F值的格子上开始探索
7关于死胡同
在我刚开始学习A星的时候,我总是会觉得,如果碰到死胡同,寻路仿佛就要终止,其实不然,A星是一个不同的搜索最小值过程的算法,除非open列表不存在,否则open列表中一定有一个是最小值的格子,只要存在这个格子,那么寻路就能继续下去,而对于最终的路径,我们是通过回溯的方式来获取,所以对于寻路,并不是说close表里的格子就都是路径了,想明白这回事,对A星就有比较明确的认识了