参考:
A星算法详解(个人认为最详细,最通俗易懂的一个版本)
最短路径A*算法原理及java代码实现(看不懂是我的失败)
算法简介
A*搜索算法结合了基于广度搜索的Dijkstra算法和贪心思想的BFS最佳优先搜索的优点设计的最短路径算法,他可以用于含有障碍物的图算法之中。其思想是首先将节点的邻近节点加入带处理队列之中,但是并不是依次处理,而是通过一个评估函数,依据该函数有优先级地对待处理队列中的节点进行处理。这样就能较为快速的找到终点。
算法思想
首先要将节点分类,分为可通行节点,不可通行节点,起始节点和终点。然后节点应该还含有的参数是父节点的指针。
从起点开始,首先搜寻与起点相邻的节点,将其加入待检查队列openlist中。然后进入循环,从openList中取出一个点,取出原则后面介绍,将该节点加入closeList中。由于当前只有起点,因此取出的就是起点。然后将与起点相连的点(不加入路障点和在closeList中的点)加入openList中,并且在加入过程中,要计算每个点的评估函数值F,该值是作为前面提到的从openList中取点原则的依据(取F最小的点)。F=G+H,其中G为从起始节点到该点所要花费的总的代价,H为该点到终点的预测代价。在加入点到openList的过程中,如果该点的父节点为空,则还要将加入点的父节点指定为当前节点;若父节点不为空(说明带加入点已经在openList之中了,此时要做到就是是否更新带加入点的状态),则要比较从当前节点到带加入点的G值还是带加入点的已有G值大,若前者大,则将更新G值和带加入点的父节点,否则不改变带加入点的状态。循环以上操作直至终点加入到openList之中。最后,根据终点的父节点指针,依次找到起始节点。
算法伪代码
main(){
openList.add(startNode)
while(!openList.isEmpty()){
node = openList.getMinFNode(); //opt1
closeList.add(node);
if(node==endNode){
isFind = true;
break;
}
addAdjacentNode(node); //opt2
}
getTrace();
}
addAdjacentNode(curNode){
nodeList = getAdjacentNode(curNode); //opt3
for(node belong nodeList){
calculate of update F of node; //opt4
openList.add(node); //opt5
}
}
优化
opt1
getMinFNode
方法可以将openList采用优先队列的方式达到快速得到最小F的点。但是优先队列存在一个问题,就是无法快速的在opt5处更新已经在openList中的节点,因此要配合HashSet使用,所有该数据结构最好自己实现。
opt2
将邻居节点插入openList中时要做一系列工作,比如寻找得到临近节点列表,计算每个临近节点的f值(包含计算H和计算/更新G),将节点加入openList。
opt3
在下面具体实现的代码中,我们采取的节点为方块形式,因此可以采用上下左右斜对角的8个方向进行查找,保证不会遗漏。但是如果对于树形结构的图,也可以采用遍历该节点的邻接链表方式,得到一系列相邻节点。
opt4
F值的计算主要是计算G(G值的计算主要是根据Dijkstra算法思想),如果该节点不在openList中,则直接计算G值并更新父节点;如果该节点在openList中,那么之前肯定计算过G值,此时就是要计算从上一节点lastNode到该节点的G值,并与原先的G值比较,如果新的G值小,则要跟新G值和将该点的父节点重新定向到lastNode上。在我们下面的实例中,G值默认为水平垂直花费为10,斜对角为14。但如果采用的是树形图,则要根据节点之间的权重进行计算(Dijkstra算法)。
H值的计算采用了最佳优先搜索的方法,在下面的实例中,我们采用的是忽略路障的曼哈顿距离来估计,当然也可以依据情况采用欧式距离,非欧距离等方式计算。对于树形图的H计算我暂时也不清楚,这也是我不打算将A星搜索算法用于树形图的原因(节点之间的连接数不确定)。
opt5
如opt1中所述,要想用优先队列,就要解决如何在加入时保证加入点不重复,且能及时更新最小F的问题。这也是我没采用优先队列而使用了HashSet的原因(虽然在opt1中要遍历获得最小值f比较慢)
算法实现
Node.java
public class Node {
int f;