Java编程实现A*算法完整代码

前言

A*搜寻算法俗称A星算法。这是一种在图形平面上,有多个节点的路径,求出最低通过成本的算法。常用于游戏中

通过二维数组构建的一个迷宫,“%”表示墙壁,A为起点,B为终点,“#”代表障碍物,“*”代表算法计算后的路径

本文实例代码结构:

% % % % % % %
% o o o o o %
% o o # o o %
% A o # o B %
% o o # o o %
% o o o o o %
% % % % % % %
=============================
经过A*算法计算后
=============================
% % % % % % %
% o o * o o %
% o * # * o %
% A o # o B %
% o o # o o %
% o o o o o %
% % % % % % % <

算法理论

算法的核心公式为:F=G+H

把地图上的节点看成一个网格。

G=从起点A,沿着产生的路径,移动到网格上指定节点的移动消耗,在这个例子里,我们令水平或者垂直移动的耗费为10,对角线方向耗费为14。我们取这些值是因为沿对角线

的距离是沿水平或垂直移动耗费的的根号2,或者约1.414倍。为了简化,我们用10和14近似。

既然我们在计算沿特定路径通往某个方格的G值,求值的方法就是取它父节点的G值,然后依照它相对父节点是对角线方向或者直角方向(非对角线),分别增加14和10。例子中这

个方法的需求会变得更多,因为我们从起点方格以外获取了不止一个方格。

H=从当前格移动到终点B的预估移动消耗。为什么叫”预估“呢,因为我们没有办法事先知道路径的长度,这里我们使用曼哈顿方法,它计算从当前格到目的格之间水平和垂直

的方格的数量总和,忽略对角线方向。然后把结果乘以10。

F的值是G和H的和,这是我们用来判断优先路径的标准,F值最小的格,我们认为是优先的路径节点。

实现步骤

算法使用java写的,先看一看节点类的内容

package a_star_search;
/**
 * 节点类
 * @author zx
 *
 */
public class Node {
  private int x; //x坐标
  private int y; //y坐标
  private String value;  //表示节点的值
  private double FValue = 0; //F值
  private double GValue = 0; //G值
  private double HValue = 0; //H值
  private boolean Reachable; //是否可到达(是否为障碍物)
  private Node PNode;   //父节点 

  public Node(int x, int y, String value, boolean reachable) {
    super();
    this.x = x;
    this.y = y;
    this.value = value;
    Reachable = reachable;
  } 

  public Node() {
    super();
  } 

  public int getX() {
    return x;
  }
  public void setX(int x) {
    this.x = x;
  }
  public int getY() {
    return y;
  }
  public void setY(int y) {
    this.y = y;
  }
  public String getValue() {
    return value;
  }
  public void setValue(String value) {
    this.value = value;
  }
  public double getFValue() {
    return FValue;
  }
  public void setFValue(double fValue) {
    FValue = fValue;
  }
  public double getGValue() {
    return GValue;
  }
  public void setGValue(double gValue) {
    GValue = gValue;
  }
  public double getHValue() {
    return HValue;
  }
  public void setHValue(double hValue) {
    HValue = hValue;
  }
  public boolean isReachable() {
    return Reachable;
  }
  public void setReachable(boolean reachable) {
    Reachable = reachable;
  }
  public Node getPNode() {
    return PNode;
  }
  public void setPNode(Node pNode) {
    PNode = pNode;
  }
} 

还需要一个地图类,在map的构造方法中,我通过创建节点的二维数组来实现一个迷宫地图,其中包括起点和终点

package a_star_search;
public class Map {
	private Node[][] map;
	//节点数组
	private Node startNode;
	//起点
	private Node endNode;
	//终点
	public Map() {
		map = new Node[7][7];
		for (int i = 0;i<7;i++){
			for (int j = 0;j<7;j++){
				map[i][j] = new Node(i,j,"o",true);
			}
		}
		for (int d = 0;d<7;d++){
			map[0][d].setValue("%");
			map[0][d].setReachable(false);
			map[d][0].setValue("%");
			map[d][0].setReachable(false);
			map[6][d].setValue("%");
			map[6][d].setReachable(false);
			map[d][6].setValue("%");
			map[d][6].setReachable(false);
		}
		map[3][1].setValue("A");
		startNode = map[3][1];
		map[3][5].setValue("B");
		endNode = map[3][5];
		for (int k = 1;k<=3;k++){
			map[k+1][3].setValue("#");
			map[k+1][3].setReachable(false);
		}
	}
	<span style="white-space:pre">  </span>//展示地图
	public void ShowMap(){
		for (int i = 0;i<7;i++){
			for (int j = 0;j<7;j++){
				System.out.print(map[i][j].getValue()+" ");
			}
			System.out.println("");
		}
	}
	public Node[][] getMap() {
		return map;
	}
	public void setMap(Node[][] map) {
		this.map = map;
	}
	public Node getStartNode() {
		return startNode;
	}
	public void setStartNode(Node startNode) {
		this.startNode = startNode;
	}
	public Node getEndNode() {
		return endNode;
	}
	public void setEndNode(Node endNode) {
		this.endNode = endNode;
	}
}

下面是最重要的AStar类

操作过程

1从起点A开始,并且把它作为待处理点存入一个“开启列表”,这是一个待检查方格的列表。

2寻找起点周围所有可到达或者可通过的方格,跳过无法通过的方格。也把他们加入开启列表。为所有这些方格保存点A作为“父方格”。当我们想描述路径的时候,父方格的资

料是十分重要的。后面会解释它的具体用途。

3从开启列表中删除起点A,把它加入到一个“关闭列表”,列表中保存所有不需要再次检查的方格。

经过以上步骤,“开启列表”中包含了起点A周围除了障碍物的所有节点。他们的父节点都是A,通过前面讲的F=G+H的公式,计算每个节点的G,H,F值,并按照F的值大小,从小

到大进行排序。并对F值最小的那个节点做以下操作

4,把它从开启列表中删除,然后添加到关闭列表中。

5,检查所有相邻格子。跳过那些不可通过的(1.在”关闭列表“中,2.障碍物),把他们添加进开启列表,如果他们还不在里面的话。把选中的方格作为新的方格的父节点。

6,如果某个相邻格已经在开启列表里了,检查现在的这条路径是否更好。换句话说,检查如果我们用新的路径到达它的话,G值是否会更低一些。如果不是,那就什么都不

做。(这里,我的代码中并没有判断)

7,我们重复这个过程,直到目标格(终点“B”)被添加进“开启列表”,说明终点B已经在上一个添加进“关闭列表”的节点的周围,只需走一步,即可到达终点B。

8,我们将终点B添加到“关闭列表”

9,最后一步,我们要将从起点A到终点B的路径表示出来。父节点的作用就显示出来了,通过“关闭列表”中的终点节点的父节点,改变其value值,顺藤摸瓜即可以显示出路径。

看看代码

package a_star_search;
import java.util.ArrayList;
public class AStar {
	/**
   * 使用ArrayList数组作为“开启列表”和“关闭列表”
   */
	ArrayList<Node> open = new ArrayList<Node>();
	ArrayList<Node> close = new ArrayList<Node>();
	/**
   * 获取H值
   * @param currentNode:当前节点
   * @param endNode:终点
   * @return
   */
	public double getHValue(Node currentNode,Node endNode){
		return (Math.abs(currentNode.getX() - endNode.getX()) + Math.abs(currentNode.getY() - endNode.getY()))*10;
	}
	/**
   * 获取G值
   * @param currentNode:当前节点
   * @return
   */
	public double getGValue(Node currentNode){
		if(currentNode.getPNode()!=null){
			if(currentNode.getX()==currentNode.getPNode().getX()||currentNode.getY()==currentNode.getPNode().getY()){
				//判断当前节点与其父节点之间的位置关系(水平?对角线)
				return currentNode.getGValue()+10;
			}
			return currentNode.getGValue()+14;
		}
		return currentNode.getGValue();
	}
	/**
   * 获取F值 : G + H
   * @param currentNode
   * @return
   */
	public double getFValue(Node currentNode){
		return currentNode.getGValue()+currentNode.getHValue();
	}
	/**
   * 将选中节点周围的节点添加进“开启列表”
   * @param node
   * @param map
   */
	public void inOpen(Node node,Map map){
		int x = node.getX();
		int y = node.getY();
		for (int i = 0;i<3;i++){
			for (int j = 0;j<3;j++){
				//判断条件为:节点为可到达的(即不是障碍物,不在关闭列表中),开启列表中不包含,不是选中节点
				if(map.getMap()[x-1+i][y-1+j].isReachable()&&!open.contains(map.getMap()[x-1+i][y-1+j])&&!(x==(x-1+i)&&y==(y-1+j))){
					map.getMap()[x-1+i][y-1+j].setPNode(map.getMap()[x][y]);
					//将选中节点作为父节点
					map.getMap()[x-1+i][y-1+j].setGValue(getGValue(map.getMap()[x-1+i][y-1+j]));
					map.getMap()[x-1+i][y-1+j].setHValue(getHValue(map.getMap()[x-1+i][y-1+j],map.getEndNode()));
					map.getMap()[x-1+i][y-1+j].setFValue(getFValue(map.getMap()[x-1+i][y-1+j]));
					open.add(map.getMap()[x-1+i][y-1+j]);
				}
			}
		}
	}
	/**
   * 使用冒泡排序将开启列表中的节点按F值从小到大排序
   * @param arr
   */
	public void sort(ArrayList<Node> arr){
		for (int i = 0;i<arr.size()-1;i++){
			for (int j = i+1;j<arr.size();j++){
				if(arr.get(i).getFValue() > arr.get(j).getFValue()){
					Node tmp = new Node();
					tmp = arr.get(i);
					arr.set(i, arr.get(j));
					arr.set(j, tmp);
				}
			}
		}
	}
	/**
   * 将节点添加进”关闭列表“
   * @param node
   * @param open
   */
	public void inClose(Node node,ArrayList<Node> open){
		if(open.contains(node)){
			node.setReachable(false);
			//设置为不可达
			open.remove(node);
			close.add(node);
		}
	}
	public void search(Map map){
		//对起点即起点周围的节点进行操作
		inOpen(map.getMap()[map.getStartNode().getX()][map.getStartNode().getY()],map);
		close.add(map.getMap()[map.getStartNode().getX()][map.getStartNode().getY()]);
		map.getMap()[map.getStartNode().getX()][map.getStartNode().getY()].setReachable(false);
		map.getMap()[map.getStartNode().getX()][map.getStartNode().getY()].setPNode(map.getMap()[map.getStartNode().getX()][map.getStartNode().getY()]);
		sort(open);
		//重复步骤
		do{
			inOpen(open.get(0), map);
			inClose(open.get(0), open);
			sort(open);
		}
		while(!open.contains(map.getMap()[map.getEndNode().getX()][map.getEndNode().getY()]));
		//知道开启列表中包含终点时,循环退出
		inClose(map.getMap()[map.getEndNode().getX()][map.getEndNode().getY()], open);
		showPath(close,map);
	}
	/**
   * 将路径标记出来
   * @param arr
   * @param map
   */
	public void showPath(ArrayList<Node> arr,Map map) {
		if(arr.size()>0){
			Node node = new Node();
			//<span style="white-space:pre">    </span>node = map.getMap()[map.getEndNode().getX()][map.getEndNode().getY()];
			//<span style="white-space:pre">    </span>while(!(node.getX() ==map.getStartNode().getX()&&node.getY() ==map.getStartNode().getY())){
			//<span style="white-space:pre">    </span>node.getPNode().setValue("*");
			//<span style="white-space:pre">    </span>node = node.getPNode();
			//<span style="white-space:pre">  </span>}
		}
		//<span style="white-space:pre">  </span>map.getMap()[map.getStartNode().getX()][map.getStartNode().getY()].setValue("A");
	}
}

最后写一个Main方法

package a_star_search; 

public class MainTest { 

  public static void main(String[] args) {
    Map map = new Map();
    AStar aStar = new AStar();
    map.ShowMap();
    aStar.search(map);
    System.out.println("=============================");
    System.out.println("经过A*算法计算后");
    System.out.println("=============================");
    map.ShowMap();
  }
} 

修改地图再测试一下,看看效果

% % % % % % %
% o o o o o %
% o o # o o %
% A o # o B %
% o o # o o %
% o o o o o %
% % % % % % %
=============================
经过A*算法计算后
=============================
% % % % % % %
% o o o o o %
% o o # o o %
% A o # o B %
% o o # o o %
% o o o o o %
% % % % % % %

总结

保证找到最短路径(最优解的)条件,关键在于估价函数h(n)的选取:估价值h(n)<=n到目标节点的距离实际值,这种情况下,搜索的点数多,搜索范围大,效率低。但能得到

最优解。如果估价值>实际值,搜索的点数少,搜索范围小,效率高,但不能保证得到最优解。

最大的感触就是:做事最忌三天打渔,两天晒网。量可以不大,但必须有连续性,贵在坚持。

希望每一个程序员,都能开心的敲着代码,做自己喜欢做的事。

以上就是本文关于Java编程实现A*算法完整代码的全部内容,希望对大家有所帮助。感兴趣的朋友可以继续参阅本站其他相关专题,如有不足之处,欢迎留言指出。

(0)

相关推荐

  • Java常用加密算法实例总结

    本文实例总结了Java常用加密算法.分享给大家供大家参考,具体如下: 项目中第一次深入地了解到加密算法的使用,现第一阶段结束,将使用到的加密算法和大家分享一下: 首先还是先给大家普及一下常用加密算法的基础知识 基本的单向加密算法 BASE64 严格地说,属于编码格式,而非加密算法 MD5(Message Digest algorithm 5,信息摘要算法) SHA(Secure Hash Algorithm,安全散列算法) 复杂的加密算法 RSA(算法的名字以发明者的名字命名:Ron Rives

  • java 算法之冒泡排序实例详解

    java 算法之冒泡排序实例详解 无人不知无人不晓的冒泡排序,据说是模仿泡泡从水中浮起跑到水面的过程. 在要排序的一组数中,对当前还未排好序的范围内的全部数,自上而下对相邻的两个数依次进行比较和调整,让较大的数往下沉,较小的往上冒.即: 每当两相邻的数比较后发现它们的排序与排序要求相反时,就将它们互换. 来看一下代码: package cn.songxinqiang.study.algorithm.sort; import java.util.Arrays; /** * 冒泡排序 * * <p>

  • Java随机数算法原理与实现方法实例详解

    本文实例讲述了Java随机数算法.分享给大家供大家参考,具体如下: 软件实现的算法都是伪随机算法,随机种子一般是系统时间 在数论中,线性同余方程是最基本的同余方程,"线性"表示方程的未知数次数是一次,即形如: ax≡b (mod n)的方程.此方程有解当且仅当 b 能够被 a 与 n 的最大公约数整除(记作 gcd(a,n) | b).这时,如果 x0 是方程的一个解,那么所有的解可以表示为: {x0+kn/d|(k∈z)} 其中 d 是a 与 n 的最大公约数.在模 n 的完全剩余系

  • Java实现与JS相同的Des加解密算法完整实例

    本文实例讲述了Java实现与JS相同的Des加解密算法.分享给大家供大家参考,具体如下: 这里演示java与js实现相同的des加解密算法,不多说,不废话,直接上代码 一.java实现 package com.lyz.base.des; import java.util.ArrayList; import java.util.List; /** * DES加密/解密 * * @Copyright Copyright (c) 2015 * @author liuyazhuang * @see DE

  • Java 蒙特卡洛算法求圆周率近似值实例详解

    起源 [1946: John von Neumann, Stan Ulam, and Nick Metropolis, all at the Los Alamos Scientific Laboratory, cook up the Metropolis algorithm, also known as the Monte Carlo method.]1946年,美国拉斯阿莫斯国家实验室的三位科学家John von Neumann,Stan Ulam 和 Nick Metropolis共同发明,

  • java算法之二分查找法的实例详解

    java算法之二分查找法的实例详解 原理 假定查找范围为一个有序数组(如升序排列),要从中查找某一元素,如果该元素在此数组中,则返回其索引,否则返回-1.通过数组长度可取出中间位置元素的索引,将其值与目标值比较,如果中间位置元素值大于目标值,则在左部分进行查找,如果中间位置值小于目标值,则在右部分进行查找,如此循环,直到结束.二分查找算法之所以快是因为它没有遍历数组的每个元素,而仅仅是查找部分元素就能找到目标或确定其不存在,当然前提是查找范围为有序数组. Java的简单实现 package me

  • java 实现MD5加密算法的简单实例

    java 实现MD5加密算法的简单实例 实现代码: import java.security.NoSuchAlgorithmException; public class MD5HashUtil { private MessageDigest md = null; private static MD5HashUtil md5 = null; private static final char[] hexChars ={'0','1','2','3','4','5','6','7','8','9'

  • java 二分法算法的实例

    java 二分法算法的实例 1.前提:二分查找的前提是需要查找的数组必须是已排序的,我们这里的实现默认为升序 2.原理:将数组分为三部分,依次是中值(所谓的中值就是数组中间位置的那个值)前,中值,中值后:将要查找的值和数组的中值进行比较,若小于中值则在中值前面找,若大于中值则在中值后面找,等于中值时直接返回.然后依次是一个递归过程,将前半部分或者后半部分继续分解为三部分.可能描述得不是很清楚,若是不理解可以去网上找.从描述上就可以看出这个算法适合用递归来实现,可以用递归的都可以用循环来实现.所以

  • Java编程实现A*算法完整代码

    前言 A*搜寻算法俗称A星算法.这是一种在图形平面上,有多个节点的路径,求出最低通过成本的算法.常用于游戏中 通过二维数组构建的一个迷宫,"%"表示墙壁,A为起点,B为终点,"#"代表障碍物,"*"代表算法计算后的路径 本文实例代码结构: % % % % % % % % o o o o o % % o o # o o % % A o # o B % % o o # o o % % o o o o o % % % % % % % % =======

  • java编程实现求解八枚银币代码分享

    1.引言 笔者在大学的算法竞赛中,遇到过这样的一个题目,现在拿出来与大家分享一下:现在有现有八枚银币abcdefgh,已知其中一枚是假币,其重量不同于真币,但不知是较轻或较重,如何使用天平以最少的比较次数,决定出哪枚是假币,并得知假币比真币较轻或较重. 2.分析 如果本题目只是很单纯的求解假币是哪一个,问题倒并不是很复杂,只需要回溯递归便可求得结果.问题的难点在意,我们需要用最少的步骤!!! 比之以前的数据结构问题,有递归,回溯,我们今天可能要接触一个新的概念,叫做树.顾名思义,数结构就是说我们

  • Java编程redisson实现分布式锁代码示例

    最近由于工作很忙,很长时间没有更新博客了,今天为大家带来一篇有关Redisson实现分布式锁的文章,好了,不多说了,直接进入主题. 1. 可重入锁(Reentrant Lock) Redisson的分布式可重入锁RLock Java对象实现了java.util.concurrent.locks.Lock接口,同时还支持自动过期解锁. public void testReentrantLock(RedissonClient redisson){ RLock lock = redisson.getL

  • Java编程实现逆波兰表达式代码示例

    逆波兰表达式 定义:传统的四则运算被称作是中缀表达式,即运算符实在两个运算对象之间的.逆波兰表达式被称作是后缀表达式,表达式实在运算对象的后面. 逆波兰表达式: a+b ---> a,b,+ a+(b-c) ---> a,b,c,-,+ a+(b-c)*d ---> a,b,c,-,d,*,+ a+d*(b-c)--->a,d,b,c,-,*,+ a=1+3 ---> a=1,3 + http=(smtp+http+telnet)/1024 写成什么呢? http=smtp,

  • Java编程Webservice指定超时时间代码详解

    WebService是一种跨编程语言和跨操作系统平台的远程调用技术 所谓远程调用,就是一台计算机a上的一个程序可以调用到另外一台计算机b上的一个对象的方法,譬如,银联提供给商场的pos刷卡系统(采用交互提问的方式来加深大家对此技术的理解). 远程调用技术有什么用呢?商场的POS机转账调用的转账方法的代码是在银行服务器上,还是在商场的pos机上呢?什么情况下可能用到远程调用技术呢?例如,amazon,天气预报系统,淘宝网,校内网,百度等把自己的系统服务以webservice服务的形式暴露出来,让第

  • Java语言实现Blowfish加密算法完整代码分享

    前几天网上突然出现流言:某东发生数据泄露12G,最终某东在一篇声明中没有否认,还算是勉强承认了吧,这件事对于一般人有什么影响.应该怎么做已经有一堆人说了,所以就不凑热闹了,咱来点对程序猿来说实际点的,说一个个人认为目前比较安全的加密算法:Blowfish. 上代码之前,先说几点Blowfish加密算法的特点: 1. 对称加密,即加密的密钥和解密的密钥是相同的: 2. 每次加密之后的结果是不同的(这也是老夫比较欣赏的一点): 3. 可逆的,和老夫之前的文章介绍的md5等摘要算法不一样,他是可逆的:

  • Java解压zip文件完整代码分享

    关于Java解压zip文件,我觉得也没啥好多说的,就是干呗..代码如下: package com.lanyuan.assembly.util; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.Enumeration; i

  • Java编程一个随机数产生模块代码分享

    java随机数的产生比较简单,可以通过 Random rand = new Random(47); System.out.println(rand.nextInt()); 产生,也可以通过以下产生: double d = Math.random(); 当然代码中前者由于使用了固定的种子47,所以每次的值都是一样的,也可以使用 Random rand = new Random(); System.out.println(rand.nextInt()); 而对于代码2则产生的是double的随机数.

  • Java编程多线程之共享数据代码详解

    本文主要总结线程共享数据的相关知识,主要包括两方面:一是某个线程内如何共享数据,保证各个线程的数据不交叉:一是多个线程间如何共享数据,保证数据的一致性. 线程范围内共享数据 自己实现的话,是定义一个Map,线程为键,数据为值,表中的每一项即是为每个线程准备的数据,这样在一个线程中数据是一致的. 例子 package com.iot.thread; import java.util.HashMap; import java.util.Map; import java.util.Random; /*

  • Java加密解密和数字签名完整代码示例

    常见的加密算法 基本的单向加密算法: BASE64严格地说,属于编码格式,而非加密算法 MD5(MessageDigestalgorithm5,信息摘要算法) SHA(SecureHashAlgorithm,安全散列算法) HMAC(HashMessageAuthenticationCode,散列消息鉴别码) 复杂的对称加密(DES.PBE).非对称加密算法: DES(DataEncryptionStandard,数据加密算法) PBE(Password-basedencryption,基于密码

随机推荐