A*算法Java简单实现
发布时间:2020-12-15 03:23:19 所属栏目:Java 来源:网络整理
导读:今天PHP站长网 52php.cn把收集自互联网的代码分享给大家,仅供参考。 package astar; import java.util.ArrayList;import java.util.Comparator;import java.util.List;import java.util.PriorityQueue; /** * A*搜索算法
|
以下代码由PHP站长网 52php.cn收集自互联网 现在PHP站长网小编把它分享给大家,仅供参考 package astar;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.PriorityQueue;
/**
* A*搜索算法,A星算法。
* 这是一种在图形平面上,有多个节点的路径,求出最低通过成本的算法。
* 常用于游戏中的NPC的移动计算,或在线游戏的BOT的移动计算上。
* 该算法像Dijkstra算法一样,可以找到一条最短路径;也像BFS一样,进行启发式的搜索。
* 在此算法中,如果以 g(n)表示从起点到任意顶点n的实际距离,
* h(n)表示任意顶点n到目标顶点的估算距离,
* 那么 A*算法的公式为:f(n)=g(n)+h(n)。 这个公式遵循以下特性:
* 如果h(n)为0,只需求出g(n),即求出起点到任意顶点n的最短路径,则转化为单源最短路径问题,即Dijkstra算法
* 如果h(n)<=“n到目标的实际距离”,则一定可以求出最优解。而且h(n)越小,需要计算的节点越多,算法效率越低。
*
* @author DBJ([email?protected])
*/
public class AStar {
/** 搜索区域 0:不能走 1:能走 */
private int[][] searchArea;
/** 记录节点状态 */
private int[][] searchAreaStatus;
private int width;
private int height;
/** 开启列表 */
private PriorityQueue<AStarNode> openList;
/** FComparator */
public static Comparator<AStarNode> fComparator = new Comparator<AStarNode>() {
@Override
public int compare(AStarNode c1,AStarNode c2) {
return (int) (c1.getF() - c2.getF());
}
};
/**
* AStart
* @param searchArea 搜索区域
* @param width 搜索区域的宽
* @param height 搜索区域的高
*/
public AStar(int[][] searchArea,int width,int height) {
this.width = width;
this.height = height;
this.searchArea = searchArea;
this.searchAreaStatus = new int[this.height][this.width];
this.openList = new PriorityQueue<>(10,fComparator);
}
/**
* 查找路径
*
* @param x1 起点A的x坐标
* @param y1 起点A的y坐标
* @param x2 终点B的x坐标
* @param y2 终点B的y坐标
* @return 起点A到终点B的路径
*/
public List<AStarNode> find(int x1,int y1,int x2,int y2) {
AStarNode startNode = new AStarNode(x1,y1);
AStarNode endNode = new AStarNode(x2,y2);
return this.find(startNode,endNode);
}
/**
* find 搜索
* @param startNode 起点
* @param endNode 终点
* @return 路径
*/
private List<AStarNode> find(AStarNode startNode,AStarNode endNode) {
List<AStarNode> resultList = new ArrayList<AStarNode>();
/* 是否找到 */
boolean findFalg = false;
/* 1.从起点A开始,并将它添加到 “开启列表”。 */
this.openList.add(startNode);
searchAreaStatus[startNode.getX()][startNode.getY()] = AStarConstants.NOTE_STATUS_OPEN;
AStarNode curNode = this.openList.poll();
while (curNode != null) {
/* c)对于当前方格临近的8个方格的每一个....(For Each) */
// System.out.println("[email?protected] curNode=" + curNode);
for (int i = 0; i < 8; i++) {
switch (i) {
case 0:// 右
check(curNode.getX(),curNode.getY() + 1,endNode,curNode,AStarConstants.COST_ORTHOGONAL); // 10
break;
case 1:// 下
check(curNode.getX() + 1,curNode.getY(),AStarConstants.COST_ORTHOGONAL); // 10
break;
case 2:// 左
check(curNode.getX(),curNode.getY() - 1,AStarConstants.COST_ORTHOGONAL); // 10
break;
case 3:// 上
check(curNode.getX() - 1,AStarConstants.COST_ORTHOGONAL); // 10
break;
case 4:// 右上
check(curNode.getX() - 1,AStarConstants.COST_DIAGONAL); // 14
break;
case 5:// 右下
check(curNode.getX() + 1,AStarConstants.COST_DIAGONAL); // 14
break;
case 6:// 左上
check(curNode.getX() - 1,AStarConstants.COST_DIAGONAL); // 14
break;
case 7:// 左下
check(curNode.getX() + 1,AStarConstants.COST_DIAGONAL); // 14
break;
} // end switch
} // end for
// 加入关闭列表
findFalg = this.addClosedList(curNode,endNode);
if (findFalg) {
break;
}
/* a)寻找开启列表上最小F值的方格。将它作为当前方格。 */
curNode = this.openList.poll();
} // while
if (findFalg) {
// 有
read(resultList,curNode);
return resultList;
} else {
/* 无法找到目标方格并且开启列表是空的时候,不存在路径。 */
return resultList;
}
}
/**
* 读取所有节点,从起点开始返
*
* @param resultList
* @param node
*/
private void read(List<AStarNode> resultList,AStarNode node) {
if (node.getParent() != null) {
read(resultList,node.getParent());
}
resultList.add(node);
}
/**
* hasNearbyUnwalkable
* @param x x坐标
* @param y y坐标
* @param parent 父
* @return
*/
private boolean hasNearbyUnwalkable(int x,int y,AStarNode parent) {
boolean bRes = false;
if (x != parent.getX() && y != parent.getY()) {
if (this.searchArea[parent.getX()][y] == AStarConstants.NOTE_UNWALKABLE) {
bRes = true;
}
if (this.searchArea[x][parent.getY()] == AStarConstants.NOTE_UNWALKABLE) {
bRes = true;
}
}
return bRes;
}
/**
* 检查 当前节点周围的节点,是否能行,是否在开启列表中,是否在关闭列表中 如果不在关闭与开启列表中则加入开启列表,如果在开启中则更新节点G值信息
*
* @param x x坐标
* @param y y坐标
* @param endNode 终点
* @param parent 父
* @param step 步长
* @return
*/
private boolean check(int x,AStarNode endNode,AStarNode parent,int step) {
// System.out.println(" [email?protected] (" + x + "," + y + ")" + parentNode);
try {
if (this.searchArea[x][y] == AStarConstants.NOTE_UNWALKABLE) {
/* 如果不能走,忽略它。*/
return false;
}
if (this.searchAreaStatus[x][y] == AStarConstants.NOTE_STATUS_CLOSED) {
/* 如果它在关闭列表上,忽略它。 */
return false;
}
/* 否则,执行以下操作。 */
if (this.searchAreaStatus[x][y] == AStarConstants.NOTE_STATUS_NONE) {
if (hasNearbyUnwalkable(x,y,parent)) {
return false;
}
/* 如果不在开启列表中,把它添加到开启列表。使当前方格成为这个方格的父。记录的方格F值,G值和H值。*/
this.addOpenList(x,parent,step);
this.searchAreaStatus[x][y] = AStarConstants.NOTE_STATUS_OPEN;
return true;
} else if (this.searchAreaStatus[x][y] == AStarConstants.NOTE_STATUS_OPEN) {
/* 如果在开启列表了,检查看看采用G值来衡量这个路径到那个方格是否是更好的。*/
this.updateOpenList(x,step);
return true;
}
} catch (ArrayIndexOutOfBoundsException e) {
return false;// 下标越界
}
return false;
}
/**
* 添加到关闭列表
*
* @param node 节点
* @param endNode 终点
* @return true:路径被发现
*/
private boolean addClosedList(AStarNode node,AStarNode endNode) {
if (node.getX() == endNode.getX() && node.getY() == endNode.getY()) {
/* 在目标方格添加到关闭列表的情况下,路径已经被发现 */
return true;
}
this.searchAreaStatus[node.getX()][node.getY()] = AStarConstants.NOTE_STATUS_CLOSED;
return false;
}
/**
* 添加到开启列表
* 使当前方格成为这个方格的父。记录的方格F值,G值和H值。
*
* @param x x坐标
* @param y y坐标
* @param endNode 终点
* @param parent 父
* @param step 步长
* @return
*/
private boolean addOpenList(int x,int step) {
/* 使当前方格成为这个方格的父。 */
AStarNode node = new AStarNode(x,parent);
/* 记录的方格F值,G值和H值。 */
this.count(node,step);
this.openList.add(node);
// System.out.println(" [email?protected] " + node + parentNode);
return true;
}
/**
* 已经在开启列表了更新节点F值
* 更低的G值意味着这是一个更好的路径。如果是这样,把方格的父改为当前方格,并重新计算方格的G值和F值。如果你保持开启列表排序F值,由于这个变化你可能需重存列表。
*
* @param x x坐标
* @param y y坐标
* @param endNode 终点
* @param parent 父
* @param step 步长
* @return
*/
private boolean updateOpenList(int x,int step) {
AStarNode node = new AStarNode(x,y);
for (AStarNode nd : this.openList) {
if (node.equals(nd)) {
node = nd;
break ;
}
}
int g = parent.getG() + step;
if (g < node.getG()) {
/* 如果是更低的G值意味着这是一个更好的路径。把方格的父改为当前方格 */
node.setParent(parent);
/* 并重新计算方格的G值和F值。*/
this.count(node,step);
/* 如果你保持开启列表按F值排序,由于这个变化你可能需重存列表。 */
this.openList.remove(node);
this.openList.add(node);
return true;
}
return false;
}
/**
* 计算GHF
*
* @param node 节点
* @param endNode 终点
* @param step 步长
*/
private void count(AStarNode node,int step) {
this.countG(node,node.getParent(),step);
this.countH(node,endNode);
this.countF(node);
}
/**
* 计算G值
* 将指定每个移动水平或垂直方成本为10,对角线移动成本为14
* 找出那个方块的父亲的G值,然后加10或14取决于它从父方格是正交(非对角线)还是对角线。
*
* @param node 节点
* @param parent 父
* @param step 步长
*/
private void countG(AStarNode node,int step) {
if (parent == null) {
node.setG(step);
} else {
node.setG(parent.getG() + step);
}
}
/**
* 计算H值
* 曼哈顿方法
* 计算从当前方块到目标方水平和垂直方向移动方块的总数
* 将总数乘以10
*
* @param node 节点
* @param endNode 终点
*/
private void countH(AStarNode node,AStarNode endNode) {
node.setH((Math.abs(endNode.getX() - node.getX()) + Math.abs(endNode.getY() - node.getY())) * 10);
}
/**
* 计算F值
* F = G + H
*
* @param node 节点
*/
private void countF(AStarNode node) {
node.setF(node.getG() + node.getH());
}
}
以上内容由PHP站长网【52php.cn】收集整理供大家参考研究 如果以上内容对您有帮助,欢迎收藏、点赞、推荐、分享。 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
