A*算法搜索地图的Java实现

       写了下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<graph.length;i++) {
			for(int j=0;j<graph[0].length;j++) {
				nodes[i][j] = new Node(i,j,graph[i][j]);
				enableFlag[i][j] = S.INIT; //将状态置为初始状态
			}
		}
		aStarAlgorithm.search(nodes);
		
		
		//测试无解地图
		Node[][] nodes2 = new Node[graph.length][graph[0].length];
		System.out.println("\n---测试无解实例---");
		enableFlag = new S[graph2.length][graph2[0].length];
		for(int i=0;i<graph2.length;i++) {
			for(int j=0;j<graph2[0].length;j++) {
				nodes2[i][j] = new Node(i,j,graph2[i][j]);
				enableFlag[i][j] = S.INIT; //将状态置为初始状态
			}
		}
		aStarAlgorithm.search(nodes2);
	}

	
	/**
	 * 开始搜索
	 * @param start_row 起始坐标的行号
	 * @param start_column 起始坐标的列号
	 */
	public void search(Node[][] graph) {
		
		
		//打印地图数据
		System.out.println("地图:");
		for(int i=0;i<graph.length;i++) {
			for(int j=0;j<graph[0].length;j++) {
				System.out.print(graph[i][j].type + " ");
			}
			System.out.println();
		}
	
		//检索地图得到起始点和目标点坐标
		for(int i=0;i<graph.length;i++) {
			for(int j=0;j<graph[i].length;j++) {
				if(graph[i][j].type == SELF) {
					start_row = i;
					start_column = j;
				} else if(graph[i][j].type == DEST) {
					dest_row = i;
					dest_column = j;
				}
			}
		}
		
		//---使用java自带的优先队列---
		Comparator<Node> comparator = new Comparator<Node>() {

			@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<Node> 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<currNode.row+2;i++) {
				for(int j=currNode.column-1;j<currNode.column+2;j++) {
					
					if(i>=0&& i<graph.length && j>=0 && j<graph[i].length //边界过滤
							&& graph[i][j].type != STONE //不是障碍物
							&& enableFlag[i][j] != S.CLOSE /*不在关闭列表中*/) {
						
						if(i != currNode.row && j != currNode.column) { //斜着移动
							if(graph[i][j].getCost() > 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

      谢谢!!

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值