astar

A星算法Java实现

一、适用场景

在一张地图中,绘制从起点移动到终点的最优路径,地图中会有障碍物,必须绕开障碍物。

二、算法思路

1. 回溯法得到路径

(如果有路径)采用“结点与结点的父节点”的关系从最终结点回溯到起点,得到路径。

2. 路径代价的估算:F = G+H

A星算法的代价计算使用了被称作是启发式的代价函数。 先说明一下各符号意义:G表示的是 从起点到当前结点的实际路径代价 (为啥叫实际?就是已经走过了,边走边将代价计算好了);H表示 当前结点到达最终结点的估计代价 (为啥叫估计?就是还没走过,不知道前面有没障碍、路通不通,所以只能用估计);F表示 当前结点所在路径从起点到最终点预估的总路径代价

G的计算方式:计算方式有挺多种的,这里我们就用这种吧,假设每个结点代表一个正方形,横竖移动距离:斜移动距离=1:1.4(根号2),我们取个整数10和14吧,也就是说当前结点G值=父节点的G+(10或14)。

H的计算方式:估价计算也有很多种方式,我们这里使用“曼哈顿”法,H=|当前结点x值-最终结点x值|+|当前结点y值-最终结点y值|("||"表示绝对值)。

如下图(图不是自己做的,从网上借来的,自己画的话~...惨不忍睹!)

3. 辅助表:Open、Close列表

在A星算法中,需要使用两个辅助表来记录结点。 一个用于 记录可被访问的结点 ,成为Open表;一个是 记录已访问过的结点 ,称为Close表。 这两个表决定了算法的结束:条件是最终结点在Close表中(找到路径)或Open表为空(找不到了路径)。

4. 移动结点、相邻结点的处理

上面的理解的话,现在就来移动当前的节点,寻找路径。

每次从Open表中取出F值最小的结点出来( 这里我们使用优先队列来处理比较好 ),作为当前结点;然后将当前结点的所有邻结点按照 邻结点规则 加入到Open表中;最后将当前结点放入Close表中,这里就是每次循环的执行内容。

邻结点规则: (1) 当邻结点不在地图中,不加入Open表; (2) 当邻结点是障碍物,不加入Open表; (3) 当邻结点在Close表中,不加入Open表; (4) 当邻结点不在Open中,加入Open表, 设该邻结点的父节点为当前结点 ; (5) **当邻结点在Open表中,我们需要做个比较:如果邻结点的G值>当前结点的G值+当前结点到这个邻结点的代价,那么修改该邻结点的父节点为当前的结点(因为在Open表中的结点除了起点,都会有父节点),修改G值=当前结点的G值+当前结点到这个邻结点的代价 **

蓝色框框表示在Close表中,绿色的框框表示在Open表中

最后回溯得到路径

三、代码实现(Java)

1. 输入

(1) 代表地图二值二维数组(0表示可通路,1表示路障)

  1. int[][] maps = {
  2. { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
  3. { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
  4. { 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0 },
  5. { 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0 },
  6. { 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0 },
  7. { 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 },
  8. { 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 }
  9. };

(2) 按照二维数组的特点,坐标原点在左上角,所以y是高,x是宽,y向下递增,x向右递增,我们将x和y封装成一个类,好传参,重写equals方法比较坐标(x,y)是不是同一个。

  1. public class Coord
  2. {
  3. public int x;
  4. public int y;
  5.  
  6. public Coord(int x, int y)
  7. {
  8. this.x = x;
  9. this.y = y;
  10. }
  11.  
  12. @Override
  13. public boolean equals(Object obj)
  14. {
  15. if (obj == null) return false;
  16. if (obj instanceof Coord)
  17. {
  18. Coord c = (Coord) obj;
  19. return x == c.x && y == c.y;
  20. }
  21. return false;
  22. }
  23. }

(3) 封装路径结点类,字段包括:坐标、G值、F值、父结点,实现Comparable接口,方便优先队列排序。

  1. public class Node implements Comparable<Node>
  2. {
  3.  
  4. public Coord coord; // 坐标
  5. public Node parent; // 父结点
  6. public int G; // G:是个准确的值,是起点到当前结点的代价
  7. public int H; // H:是个估值,当前结点到目的结点的估计代价
  8.  
  9. public Node(int x, int y)
  10. {
  11. this.coord = new Coord(x, y);
  12. }
  13.  
  14. public Node(Coord coord, Node parent, int g, int h)
  15. {
  16. this.coord = coord;
  17. this.parent = parent;
  18. G = g;
  19. H = h;
  20. }
  21.  
  22. @Override
  23. public int compareTo(Node o)
  24. {
  25. if (o == null) return -1;
  26. if (G + H > o.G + o.H)
  27. return 1;
  28. else if (G + H < o.G + o.H) return -1;
  29. return 0;
  30. }
  31. }

(4) 最后一个数据结构是A星算法输入的所有数据,封装在一起,传参方便。 :grin:

  1. public class MapInfo
  2. {
  3. public int[][] maps; // 二维数组的地图
  4. public int width; // 地图的宽
  5. public int hight; // 地图的高
  6. public Node start; // 起始结点
  7. public Node end; // 最终结点
  8.  
  9. public MapInfo(int[][] maps, int width, int hight, Node start, Node end)
  10. {
  11. this.maps = maps;
  12. this.width = width;
  13. this.hight = hight;
  14. this.start = start;
  15. this.end = end;
  16. }
  17. }

2. 处理

(1) 在算法里需要定义几个常量来确定:二维数组中哪个值表示障碍物、二维数组中绘制路径的代表值、计算G值需要的横纵移动代价和斜移动代价。

  1. public final static int BAR = 1; // 障碍值
  2. public final static int PATH = 2; // 路径
  3. public final static int DIRECT_VALUE = 10; // 横竖移动代价
  4. public final static int OBLIQUE_VALUE = 14; // 斜移动代价

(2) 定义两个辅助表:Open表和Close表。Open表的使用是需要取最小值,在这里我们使用Java工具包中的优先队列PriorityQueue,Close只是用来保存结点,没其他特殊用途,就用ArrayList。

  1. Queue<Node> openList = new PriorityQueue<Node>(); // 优先队列(升序)
  2. List<Node> closeList = new ArrayList<Node>();

(3) 定义几个布尔判断方法:最终结点的判断、结点能否加入open表的判断、结点是否在Close表中的判断。

  1. /**
  2. * 判断结点是否是最终结点
  3. */
  4. private boolean isEndNode(Coord end,Coord coord)
  5. {
  6. return coord != null && end.equals(coord);
  7. }
  8.  
  9. /**
  10. * 判断结点能否放入Open列表
  11. */
  12. private boolean canAddNodeToOpen(MapInfo mapInfo,int x, int y)
  13. {
  14. // 是否在地图中
  15. if (x < 0 || x >= mapInfo.width || y < 0 || y >= mapInfo.hight) return false;
  16. // 判断是否是不可通过的结点
  17. if (mapInfo.maps[y][x] == BAR) return false;
  18. // 判断结点是否存在close表
  19. if (isCoordInClose(x, y)) return false;
  20.  
  21. return true;
  22. }
  23.  
  24. /**
  25. * 判断坐标是否在close表中
  26. */
  27. private boolean isCoordInClose(Coord coord)
  28. {
  29. return coord!=null&&isCoordInClose(coord.x, coord.y);
  30. }
  31.  
  32. /**
  33. * 判断坐标是否在close表中
  34. */
  35. private boolean isCoordInClose(int x, int y)
  36. {
  37. if (closeList.isEmpty()) return false;
  38. for (Node node : closeList)
  39. {
  40. if (node.coord.x == x && node.coord.y == y)
  41. {
  42. return true;
  43. }
  44. }
  45. return false;
  46. }

(4) 计算H值,“曼哈顿” 法,坐标分别取差值相加

  1. private int calcH(Coord end,Coord coord)
  2. {
  3. return Math.abs(end.x - coord.x) + Math.abs(end.y - coord.y);
  4. }

(5) 从Open列表中查找结点

  1. private Node findNodeInOpen(Coord coord)
  2. {
  3. if (coord == null || openList.isEmpty()) return null;
  4. for (Node node : openList)
  5. {
  6. if (node.coord.equals(coord))
  7. {
  8. return node;
  9. }
  10. }
  11. return null;
  12. }

(6) 添加邻结点到Open表

  1. /**
  2. * 添加所有邻结点到open表
  3. */
  4. private void addNeighborNodeInOpen(MapInfo mapInfo,Node current)
  5. {
  6. int x = current.coord.x;
  7. int y = current.coord.y;
  8. // 左
  9. addNeighborNodeInOpen(mapInfo,current, x - 1, y, DIRECT_VALUE);
  10. // 上
  11. addNeighborNodeInOpen(mapInfo,current, x, y - 1, DIRECT_VALUE);
  12. // 右
  13. addNeighborNodeInOpen(mapInfo,current, x + 1, y, DIRECT_VALUE);
  14. // 下
  15. addNeighborNodeInOpen(mapInfo,current, x, y + 1, DIRECT_VALUE);
  16. // 左上
  17. addNeighborNodeInOpen(mapInfo,current, x - 1, y - 1, OBLIQUE_VALUE);
  18. // 右上
  19. addNeighborNodeInOpen(mapInfo,current, x + 1, y - 1, OBLIQUE_VALUE);
  20. // 右下
  21. addNeighborNodeInOpen(mapInfo,current, x + 1, y + 1, OBLIQUE_VALUE);
  22. // 左下
  23. addNeighborNodeInOpen(mapInfo,current, x - 1, y + 1, OBLIQUE_VALUE);
  24. }
  25.  
  26. /**
  27. * 添加一个邻结点到open表
  28. */
  29. private void addNeighborNodeInOpen(MapInfo mapInfo,Node current, int x, int y, int value)
  30. {
  31. if (canAddNodeToOpen(mapInfo,x, y))
  32. {
  33. Node end=mapInfo.end;
  34. Coord coord = new Coord(x, y);
  35. int G = current.G + value; // 计算邻结点的G值
  36. Node child = findNodeInOpen(coord);
  37. if (child == null)
  38. {
  39. int H=calcH(end.coord,coord); // 计算H值
  40. if(isEndNode(end.coord,coord))
  41. {
  42. child=end;
  43. child.parent=current;
  44. child.G=G;
  45. child.H=H;
  46. }
  47. else
  48. {
  49. child = new Node(coord, current, G, H);
  50. }
  51. openList.add(child);
  52. }
  53. else if (child.G > G)
  54. {
  55. child.G = G;
  56. child.parent = current;
  57. // 重新调整堆
  58. openList.add(child);
  59. }
  60. }
  61. }

(7) 回溯法绘制路径

  1. private void drawPath(int[][] maps, Node end)
  2. {
  3. if(end==null||maps==null) return;
  4. System.out.println("总代价:" + end.G);
  5. while (end != null)
  6. {
  7. Coord c = end.coord;
  8. maps[c.y][c.x] = PATH;
  9. end = end.parent;
  10. }
  11. }

(8) 开始算法,循环移动结点寻找路径,设定循环结束条件,Open表为空或者最终结点在Close表

  1. public void start(MapInfo mapInfo)
  2. {
  3. if(mapInfo==null) return;
  4. // clean
  5. openList.clear();
  6. closeList.clear();
  7. // 开始搜索
  8. openList.add(mapInfo.start);
  9. moveNodes(mapInfo);
  10. }
  11.  
  12. /**
  13. * 移动当前结点
  14. */
  15. private void moveNodes(MapInfo mapInfo)
  16. {
  17. while (!openList.isEmpty())
  18. {
  19. if (isCoordInClose(mapInfo.end.coord))
  20. {
  21. drawPath(mapInfo.maps, mapInfo.end);
  22. break;
  23. }
  24. Node current = openList.poll();
  25. closeList.add(current);
  26. addNeighborNodeInOpen(mapInfo,current);
  27. }
  28. }

Java开源-astar:A 星算法的更多相关文章

  1. A星算法(Java实现)

    一.适用场景 在一张地图中.绘制从起点移动到终点的最优路径,地图中会有障碍物.必须绕开障碍物. 二.算法思路 1. 回溯法得到路径 (假设有路径)採用"结点与结点的父节点"的关系从 ...

  2. JAVA根据A星算法规划起点到终点二维坐标的最短路径

    工具类 AStarUtil.java import java.util.*; import java.util.stream.Collectors; /** * A星算法工具类 */ public c ...

  3. 算法 A-Star(A星)寻路

    一.简介 在游戏中,有一个很常见地需求,就是要让一个角色从A点走向B点,我们期望是让角色走最少的路.嗯,大家可能会说,直线就是最短的.没错,但大多数时候,A到B中间都会出现一些角色无法穿越的东西,比如 ...

  4. 算法起步之A星算法

    原文:算法起步之A星算法 用途: 寻找最短路径,优于bfs跟dfs 描述: 基本描述是,在深度优先搜索的基础上,增加了一个启发式算法,在选择节点的过程中,不是盲目选择,而是有目的的选的,F=G+H,f ...

  5. 11大Java开源中文分词器的使用方法和分词效果对比

    本文的目标有两个: 1.学会使用11大Java开源中文分词器 2.对比分析11大Java开源中文分词器的分词效果 本文给出了11大Java开源中文分词的使用方法以及分词结果对比代码,至于效果哪个好,那 ...

  6. Java开源GIS系统

     uDig  基于Eclipse RCP的uDig开源项目既是一个GeoSpatial应用程序也是一个平台开发者可通过这个平台来创建新的在uDig基础上衍生的应用程序,uDig是Web地理信息系统的一 ...

  7. java开源资源

    开到一遍不错的java开源整理,摘录一下,后续遇到好的继续更新. 构建 这里搜集了用来构建应用程序的工具. Apache Maven:Maven使用声明进行构建并进行依赖管理,偏向于使用约定而不是配置 ...

  8. 用Java开源项目JOONE实现人工智能编程

    http://www.robotsky.com/ZhiN/MoS/2011-08-25/13142461416649.html 用Java开源项目JOONE实现人工智能编程 https://sourc ...

  9. 如何用70行Java代码实现深度神经网络算法

    http://www.tuicool.com/articles/MfYjQfV 如何用70行Java代码实现深度神经网络算法 时间 2016-02-18 10:46:17  ITeye 原文  htt ...

随机推荐

  1. 关于typecho,404页面错误

    之前用typecho,但是没发现404错误页面; 现在只要发布文章就提示404页面错误. 解决方法 点击发布日期,将发布日期的分向后拖动几分钟: 然后发布,发现404错误不见了: 我是遇到这种情况了不 ...

  2. JS事件监听的添加方法

    一. 我们一般在的事件添加时是这样做的: elm.onclick = function( ) { //handler } 这样的写法兼容主流的浏览器,但是存在一个问题,当同一个elm绑定多个事件时,只 ...

  3. 02Del.ashx(删除班级)

    using System; using System.Collections.Generic; using System.Linq; using System.Web; using WebHelper ...

  4. Android--aapt命令

    1.aapt l[ist] [-v] [-a] file.{zip,jar,apk} 释义:列出压缩文件中的内容 aapt l xxx.apk:简单的罗列压缩文件中每一项的内容 aapt l -v x ...

  5. Oracle等待事件之db file sequential read/ db file parallel read

    1.产生原因 db file sequential read这个是非常常见的I/O 相关的等待事件.表示发生了与索引扫描相关的等待.意味着I/O 出现了问题,通常表示I/O竞争或者I/O 需求太多. ...

  6. Flink简介及使用

    一.Flink概述 官网:https://flink.apache.org/ mapreduce-->maxcompute HBase-->部门 quickBI DataV Hive--& ...

  7. Python开发【模块】:BeautifulSoup

    BeautifulSoup BeautifulSoup是一个模块,该模块用于接收一个HTML或XML字符串,然后将其进行格式化,之后遍可以使用他提供的方法进行快速查找指定元素,从而使得在HTML或XM ...

  8. android(十五) FTP的两种工作模式

    (一)PORT(主动)方式的连接过程是:客户端向服务器的FTP端口(默认是21)发送连接请求,服务器接受连接,建立一条命令链 当需要传送数据时,客户端在命令链路上用 PORT命令告诉服务器:“我打开了 ...

  9. CCScene,CCLayer,CCSprite,CCDirector

    一.CCScene : 游戏中不同的画面可以用不同的场景展示出来,大致的可以分为以下的几类场景: 1. 展示类场景.游戏开场画面,游戏简介,胜利以及失败提示,帮助. 2. 选择类场景.主菜单,游戏设置 ...

  10. 【Loadrunner】LR破解版录制手机脚本

    LR破解版录制手机脚本          最近在网上听到好多童鞋都在问如何用LR做手机性能测试,恰好自己对这方面也挺感兴趣,经过查阅很多资料,形成此文档以做备注~!如果有感觉我写的不对的地方,敬请指正 ...