写了下A*(读作A星,或者A Star)算法的地图搜索,作为一个算法新手来说,感觉真的很好啊,因为据说在使用一个合理的估价函数的前提下A*算法能够得到最优解。我会说在检查完运行结果后,我的内心不经涌起了一股成就感吗?
什么是A*算法,个人觉得A*算法是基于Dijkstra算法的一种优化,同样是基于广搜,并有一个当前列表检索列表,并不断的从优先队列中移除已完成节点。但是A*算法中多了一个开放队列,我想正是因为这个开放队列的存在,才使得A*算法能够求的最优解,因为最优解总是会包含在这个开放队列中的。
A*算法的标准特征是包含开放列表、关闭列表、估价函数等。
package com.wly.algorithmbase.search;
import java.util.Comparator;
import java.util.PriorityQueue;
public class AStarAlgorithm {
//包含通路的地图测试实例
private static int[][] graph = {
{0,0,2,0,0,0,0,2,0,0},
{0,0,0,0,0,0,0,2,0,0},
{0,0,2,0,0,0,0,2,0,0},
{0,0,2,0,0,0,0,2,0,0},
{0,0,0,0,0,0,0,0,0,0},
{0,2,0,0,0,0,0,2,0,0},
{1,2,0,0,0,0,0,2,0,3},
{0,2,0,0,0,0,0,2,0,0},
{0,0,0,0,0,0,0,2,0,0}
};
//不包含通路的地图测试实例
private static int[][] graph2 = {
{0,0,2,0,0,0,0,2,0,0},
{0,0,0,0,0,0,0,2,0,0},
{0,0,2,0,0,0,0,2,0,0},
{0,0,2,0,0,0,0,2,0,0},
{0,0,0,0,0,0,0,0,0,0},
{0,2,0,0,0,0,0,2,0,2},
{1,2,0,0,0,0,0,2,2,2},
{0,2,0,0,0,0,0,2,0,3},
{0,0,0,0,0,0,0,2,0,0}
};
static int start_row = 0;
static int start_column = 0;
static int dest_row = 0;
static int dest_column = 0;
private static S[][] enableFlag; //用来模拟"开放列表","关闭列表"的标记数组
private static enum S{ //对应三个状态:初始状态中,开放列表中,关闭列表中
INIT,OEPN,CLOSE
};
private static int PRICE_HW = 10; //水平、垂直移动1单位代价
private static int PRICE_X = 14; //斜着移动1.44的代价
private final static int ROLD = 0; //通路
private final static int SELF = 1; //自己
private final static int STONE = 2; //障碍物
private final static int DEST = 3; //目标
public static void main(String[] args) {
AStarAlgorithm aStarAlgorithm = new AStarAlgorithm();
//测试有解地图
System.out.println("---测试有解实例---");
Node[][] nodes = new Node[graph.length][graph[0].length];
enableFlag = new S[graph.length][graph[0].length];
for(int i=0;i comparator = new Comparator() {
@Override
public int compare(Node o1, Node o2) {
if((o1.getCost()) > (o2.getCost())) {
return 1;
} else if((o1.getCost()) == (o2.getCost())) {
return 0;
} else {
return -1;
}
}
};
PriorityQueue queue = new PriorityQueue<>(12,comparator);
//将起点放置到开放列表中
queue.add(new Node(start_row, start_column,1));
enableFlag[start_row][start_column] = S.OEPN; //将起点打上开放标记
//搜索不到通路(开放列表为空),或者搜索到了通路(终点位于开放列表中)
while(!queue.isEmpty() &&
enableFlag[dest_row][dest_column] != S.OEPN) {
Node currNode = queue.poll();
enableFlag[currNode.row][currNode.column] = S.CLOSE; //放到关闭列表
for(int i=currNode.row -1;i=0&& i=0 && j currNode.getCost() + PRICE_X) {
graph[i][j].setG(currNode.getG() + PRICE_X);
}
} else { //水平、垂直移动
if(graph[i][j].getCost() > currNode.getCost() + PRICE_HW) {
graph[i][j].setG(currNode.getG() + PRICE_HW);
}
}
if(enableFlag[i][j] == S.INIT) {
queue.add(graph[i][j]);
enableFlag[i][j] = S.OEPN;
}
}
}
}
System.out.print(currNode.row + "," + currNode.column + "=>");
}
if(queue.isEmpty()) {
System.out.println("\n---未搜索到从起点到终点的有效通路!");
} else {
System.out.print(dest_row + "," + dest_column);
System.out.println("\n---搜索到从起点到终点的有效通路!");
}
}
/**
* 节点类
* @author wly
*
*/
static class Node {
Node parentNode; //持有父节点的引用
int g; //搜索当前节点已发生的开销
int row,column; //坐标
int type; //当前节点的类型(障碍物、道路等)
public Node(int row, int column,int type) {
super();
this.row = row;
this.column = column;
this.type = type;
}
public int getG() {
return g;
}
public void setG(int g) {
this.g = g;
}
public int getCost() {
return getHValue() + g;
}
/**
* 估算函数
* @return
*/
private int getHValue() {
return (Math.abs(row - dest_row) + Math.abs(column - dest_column)) * PRICE_HW;
}
}
}
运行结果:
---测试有解实例---
地图:
0 0 2 0 0 0 0 2 0 0
0 0 0 0 0 0 0 2 0 0
0 0 2 0 0 0 0 2 0 0
0 0 2 0 0 0 0 2 0 0
0 0 0 0 0 0 0 0 0 0
0 2 0 0 0 0 0 2 0 0
1 2 0 0 0 0 0 2 0 3
0 2 0 0 0 0 0 2 0 0
0 0 0 0 0 0 0 2 0 0
6,0=>5,0=>7,0=>4,1=>5,2=>6,3=>6,4=>6,5=>6,6=>5,6=>7,6=>4,7=>5,8=>6,9
---搜索到从起点到终点的有效通路!
---测试无解实例---
地图:
0 0 2 0 0 0 0 2 0 0
0 0 0 0 0 0 0 2 0 0
0 0 2 0 0 0 0 2 0 0
0 0 2 0 0 0 0 2 0 0
0 0 0 0 0 0 0 0 0 0
0 2 0 0 0 0 0 2 0 2
1 2 0 0 0 0 0 2 2 2
0 2 0 0 0 0 0 2 0 3
0 0 0 0 0 0 0 2 0 0
6,0=>7,0=>8,1=>7,2=>7,3=>7,4=>7,5=>7,6=>6,6=>8,6=>5,6=>4,7=>5,8=>4,9=>4,8=>3,9=>2,9=>3,8=>1,9=>8,5=>6,5=>4,6=>5,5=>8,4=>2,8=>5,4=>0,9=>4,4=>5,3=>6,4=>1,8=>4,5=>8,3=>6,3=>3,6=>2,6=>2,5=>5,2=>3,4=>0,8=>1,6=>8,2=>3,5=>6,2=>4,3=>8,0=>2,4=>0,6=>4,2=>3,3=>1,5=>5,0=>0,4=>4,0=>3,1=>1,4=>2,3=>0,5=>4,1=>2,1=>0,3=>3,0=>1,2=>1,1=>1,3=>2,0=>0,1=>1,0=>0,0=>
---未搜索到从起点到终点的有效通路!
说明几点:
1、代码中用一个状态标记和枚举变量代替了当前节点出于"开放列表","关闭列表"的状态。
2、代码中使用了Java自带的PriorityQueue来做优先队列,这个PriorityQueue是在JDK1.7中新增的,若在1.6的JDK上会报错。当然,读者也可以参考笔者的另一篇博文:使用二叉堆实现优先队列。
3、A*的关键在于"开放队列"的思想以及估价函数的建立,估价函数不能太大,这样会导致搜索速度不高,也不能太小,因为这样可能导致搜索不到最优解。
4、And,如果你对A*算法还不是很了解,笔者这里有秘籍:猛戳下载。
O啦~~~
转载请保留出处:http://blog.csdn.net/u011638883/article/details/17245371
谢谢!!