这种写法比较垃圾,表现在每次搜索一个点要遍历整个地图那么大的数组,如果地图为256*256,每次搜索都要执行65535次,如果遍历多个点就是n*65535,速度上实在是太垃圾了
简单说下思路,以后补充算法
优化重点在在open表和close表的遍历上,这两个地方优化后,astar会大量提速
close只用来查询所以可以用hash这样就避免了遍历
open首先用来查询是否有相同的点如果有会比较替换F值,其次用来遍历查询最小点,如果用优先级队列加hash可以减少2次遍历,但是相同点替换F值和父节点就没办法了,只能遍历一次(但一般找到该找的点就可以break,不需要完整遍历)
如果用java,c#,由于有垃圾回收机制,为了避免产生过多垃圾降低垃圾回收压力,可以用空闲对象池来避免频繁创建节点,控制内存泄露
原作者是http://www.codefans.net的JAROD
之所以说这个A-star算法高效,是因为它的open-list和close-list使用的完全是静态数组,这样就极大地降低了入栈出栈的负担。这个代码非常值得推荐。
用法很简单:
route_pt[] result = null;
// result_pt是一个简单类,它只有两个成员变量:int x和int y。
// 参数说明:map是一个二维数组,具体见程序注释
AStarRoute2 asr = new AStarRoute2(map, start_x, start_y, end_x, end_y);
try {
result = asr.getResult();
}catch (Exception ex) {
}
如果result != null那么寻路就成功了。
注意:最后获得的路径,是从终点指向起点的。您可以把两组参数倒过来传,以获得正常方向的返回值。
本人对JAROD的代码略作修改,主要是用循环取代了递归,避免栈溢出。
这是本人修改后的八方向寻路算法。如下:
import java.util.ArrayList;
public class AStarRoute2 {
private int[][] map; // 地图矩阵,0表示能通过,1表示不能通过
private int map_w; // 地图宽度
private int map_h; // 地图高度
private int start_x; // 起点坐标X
private int start_y; // 起点坐标Y
private int goal_x; // 终点坐标X
private int goal_y; // 终点坐标Y
private boolean closeList[][]; // 关闭列表
public int openList[][][]; // 打开列表
private int openListLength;
private static final int EXIST = 1;
private static final int NOT_EXIST = 0;
private static final int ISEXIST = 0;
private static final int EXPENSE = 1; // 自身的代价
private static final int DISTANCE = 2; // 距离的代价
private static final int COST = 3; // 消耗的总代价
private static final int FATHER_DIR = 4; // 父节点的方向
public static final int DIR_NULL = 0;
public static final int DIR_DOWN = 1; // 方向:下
public static final int DIR_UP = 2; // 方向:上
public static final int DIR_LEFT = 3; // 方向:左
public static final int DIR_RIGHT = 4; // 方向:右
public static final int DIR_UP_LEFT = 5;
public static final int DIR_UP_RIGHT = 6;
public static final int DIR_DOWN_LEFT = 7;
public static final int DIR_DOWN_RIGHT = 8;
private int astar_counter; // 算法嵌套深度
private boolean isFound; // 是否找到路径
public AStarRoute2(int[][] mx, int sx, int sy, int gx, int gy){
start_x = sx;
start_y = sy;
goal_x = gx;
goal_y = gy;
map = mx;
map_w = mx.length;
map_h = mx[0].length;
astar_counter = 5000;
initCloseList();
initOpenList(goal_x, goal_y);
}
// 得到地图上这一点的消耗值
private int getMapExpense(int x, int y, int dir)
{
if(dir < 5){
return 10;
}else{
return 14;
}
}
// 得到距离的消耗值
private int getDistance(int x, int y, int ex, int ey)
{
return 10 * (Math.abs(x - ex) + Math.abs(y - ey));
}
// 得到给定坐标格子此时的总消耗值
private int getCost(int x, int y)
{
return openList[x][y][COST];
}
// 开始寻路
public void searchPath()
{
addOpenList(start_x, start_y);
aStar(start_x, start_y);
}
// 寻路
private void aStar(int x, int y)
{
// 控制算法深度
for(int t = 0; t < astar_counter; t++){
if(((x == goal_x) && (y == goal_y))){
isFound = true;
return;
}
else if((openListLength == 0)){
isFound = false;
return;
}
removeOpenList(x, y);
addCloseList(x, y);
// 该点周围能够行走的点
addNewOpenList(x, y, x, y + 1, DIR_UP);
addNewOpenList(x, y, x, y - 1, DIR_DOWN);
addNewOpenList(x, y, x - 1, y, DIR_RIGHT);
addNewOpenList(x, y, x + 1, y, DIR_LEFT);
addNewOpenList(x, y, x + 1, y + 1, DIR_UP_LEFT);
addNewOpenList(x, y, x - 1, y + 1, DIR_UP_RIGHT);
addNewOpenList(x, y, x + 1, y - 1, DIR_DOWN_LEFT);
addNewOpenList(x, y, x - 1, y - 1, DIR_DOWN_RIGHT);
// 找到估值最小的点,进行下一轮算法
int cost = 0x7fffffff;
for(int i = 0; i < map_w; i++){
for(int j = 0; j < map_h; j++){
if(openList[i][j][ISEXIST] == EXIST){
if(cost > getCost(i, j)){
cost = getCost(i, j);
x = i;
y = j;
}
}
}
}
}
// 算法超深
isFound = false;
return;
}
// 添加一个新的节点
private void addNewOpenList(int x, int y, int newX, int newY, int dir)
{
if(isCanPass(newX, newY)){
if(openList[newX][newY][ISEXIST] == EXIST){
if(openList[x][y][EXPENSE] + getMapExpense(newX, newY, dir) <
openList[newX][newY][EXPENSE]){
setFatherDir(newX, newY, dir);
setCost(newX, newY, x, y, dir);
}
}else{
addOpenList(newX, newY);
setFatherDir(newX, newY, dir);
setCost(newX, newY, x, y, dir);
}
}
}
// 设置消耗值
private void setCost(int x, int y, int ex, int ey, int dir)
{
openList[x][y][EXPENSE] = openList[ex][ey][EXPENSE] + getMapExpense(x, y, dir);
openList[x][y][DISTANCE] = getDistance(x, y, ex, ey);
openList[x][y][COST] = openList[x][y][EXPENSE] + openList[x][y][DISTANCE];
}
// 设置父节点方向
private void setFatherDir(int x, int y, int dir)
{
openList[x][y][FATHER_DIR] = dir;
}
// 判断一个点是否可以通过
private boolean isCanPass(int x, int y)
{
// 超出边界
if(x < 0 || x >= map_w || y < 0 || y >= map_h){
return false;
}
// 地图不通
if(map[x][y] != 0){
return false;
}
// 在关闭列表中
if(isInCloseList(x, y)){
return false;
}
return true;
}
// 移除打开列表的一个元素
private void removeOpenList(int x, int y)
{
if(openList[x][y][ISEXIST] == EXIST){
openList[x][y][ISEXIST] = NOT_EXIST;
openListLength--;
}
}
// 判断一点是否在关闭列表中
private boolean isInCloseList(int x, int y)
{
return closeList[x][y];
}
// 添加关闭列表
private void addCloseList(int x, int y)
{
closeList[x][y] = true;
}
// 添加打开列表
private void addOpenList(int x, int y)
{
if(openList[x][y][ISEXIST] == NOT_EXIST){
openList[x][y][ISEXIST] = EXIST;
openListLength++;
}
}
// 初始化关闭列表
private void initCloseList()
{
closeList = new boolean[map_w][map_h];
for(int i = 0; i < map_w; i++){
for(int j = 0; j < map_h; j++){
closeList[i][j] = false;
}
}
}
// 初始化打开列表
private void initOpenList(int ex, int ey)
{
openList = new int[map_w][map_h][5];
for(int i = 0; i < map_w; i++){
for(int j = 0; j < map_h; j++){
openList[i][j][ISEXIST] = NOT_EXIST;
openList[i][j][EXPENSE] = getMapExpense(i, j, DIR_NULL);
openList[i][j][DISTANCE] = getDistance(i, j, ex, ey);
openList[i][j][COST] = openList[i][j][EXPENSE] + openList[i][j][DISTANCE];
openList[i][j][FATHER_DIR] = DIR_NULL;
}
}
openListLength = 0;
}
// 获得寻路结果
public route_pt[] getResult(){
route_pt[] result;
ArrayList<route_pt> route;
searchPath();
if(! isFound){
return null;
}
route = new ArrayList<route_pt>();
// openList是从目标点向起始点倒推的。
int iX = goal_x;
int iY = goal_y;
while((iX != start_x || iY != start_y)){
route.add(new route_pt(iX, iY));
switch(openList[iX][iY][FATHER_DIR]){
case DIR_DOWN: iY++; break;
case DIR_UP: iY--; break;
case DIR_LEFT: iX--; break;
case DIR_RIGHT: iX++; break;
case DIR_UP_LEFT: iX--; iY--; break;
case DIR_UP_RIGHT: iX++; iY--; break;
case DIR_DOWN_LEFT: iX--; iY++; break;
case DIR_DOWN_RIGHT: iX++; iY++; break;
}
}
int size = route.size();
result = new route_pt[size];
for(int i = 0; i < size; i++){
result[i] = new route_pt((route_pt)route.get(i));
}
return result;
}
}