一、适用场景

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

二、算法思路

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表示路障)

int[][] maps = {
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0 },
{ 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0 },
{ 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 },
{ 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 }
};

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

public class Coord
{
public int x;
public int y; public Coord(int x, int y)
{
this.x = x;
this.y = y;
} @Override
public boolean equals(Object obj)
{
if (obj == null) return false;
if (obj instanceof Coord)
{
Coord c = (Coord) obj;
return x == c.x && y == c.y;
}
return false;
}
}

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

public class Node implements Comparable<Node>
{ public Coord coord; // 坐标
public Node parent; // 父结点
public int G; // G:是个准确的值。是起点到当前结点的代价
public int H; // H:是个估值。当前结点到目的结点的预计代价 public Node(int x, int y)
{
this.coord = new Coord(x, y);
} public Node(Coord coord, Node parent, int g, int h)
{
this.coord = coord;
this.parent = parent;
G = g;
H = h;
} @Override
public int compareTo(Node o)
{
if (o == null) return -1;
if (G + H > o.G + o.H)
return 1;
else if (G + H < o.G + o.H) return -1;
return 0;
}
}

(4) 最后一个数据结构是A星算法输入的全部数据,封装在一起。传參方便。

:grin:

public class MapInfo
{
public int[][] maps; // 二维数组的地图
public int width; // 地图的宽
public int hight; // 地图的高
public Node start; // 起始结点
public Node end; // 终于结点 public MapInfo(int[][] maps, int width, int hight, Node start, Node end)
{
this.maps = maps;
this.width = width;
this.hight = hight;
this.start = start;
this.end = end;
}
}

2. 处理

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

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

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

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

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

    /**
* 推断结点是否是终于结点
*/
private boolean isEndNode(Coord end,Coord coord)
{
return coord != null && end.equals(coord);
} /**
* 推断结点是否能放入Open列表
*/
private boolean canAddNodeToOpen(MapInfo mapInfo,int x, int y)
{
// 是否在地图中
if (x < 0 || x >= mapInfo.width || y < 0 || y >= mapInfo.hight) return false;
// 推断是否是不可通过的结点
if (mapInfo.maps[y][x] == BAR) return false;
// 推断结点是否存在close表
if (isCoordInClose(x, y)) return false; return true;
} /**
* 推断坐标是否在close表中
*/
private boolean isCoordInClose(Coord coord)
{
return coord!=null&&isCoordInClose(coord.x, coord.y);
} /**
* 推断坐标是否在close表中
*/
private boolean isCoordInClose(int x, int y)
{
if (closeList.isEmpty()) return false;
for (Node node : closeList)
{
if (node.coord.x == x && node.coord.y == y)
{
return true;
}
}
return false;
}

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

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

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

private Node findNodeInOpen(Coord coord)
{
if (coord == null || openList.isEmpty()) return null;
for (Node node : openList)
{
if (node.coord.equals(coord))
{
return node;
}
}
return null;
}

(6) 增加邻结点到Open表

/**
* 增加全部邻结点到open表
*/
private void addNeighborNodeInOpen(MapInfo mapInfo,Node current)
{
int x = current.coord.x;
int y = current.coord.y;
// 左
addNeighborNodeInOpen(mapInfo,current, x - 1, y, DIRECT_VALUE);
// 上
addNeighborNodeInOpen(mapInfo,current, x, y - 1, DIRECT_VALUE);
// 右
addNeighborNodeInOpen(mapInfo,current, x + 1, y, DIRECT_VALUE);
// 下
addNeighborNodeInOpen(mapInfo,current, x, y + 1, DIRECT_VALUE);
// 左上
addNeighborNodeInOpen(mapInfo,current, x - 1, y - 1, OBLIQUE_VALUE);
// 右上
addNeighborNodeInOpen(mapInfo,current, x + 1, y - 1, OBLIQUE_VALUE);
// 右下
addNeighborNodeInOpen(mapInfo,current, x + 1, y + 1, OBLIQUE_VALUE);
// 左下
addNeighborNodeInOpen(mapInfo,current, x - 1, y + 1, OBLIQUE_VALUE);
} /**
* 增加一个邻结点到open表
*/
private void addNeighborNodeInOpen(MapInfo mapInfo,Node current, int x, int y, int value)
{
if (canAddNodeToOpen(mapInfo,x, y))
{
Node end=mapInfo.end;
Coord coord = new Coord(x, y);
int G = current.G + value; // 计算邻结点的G值
Node child = findNodeInOpen(coord);
if (child == null)
{
int H=calcH(end.coord,coord); // 计算H值
if(isEndNode(end.coord,coord))
{
child=end;
child.parent=current;
child.G=G;
child.H=H;
}
else
{
child = new Node(coord, current, G, H);
}
openList.add(child);
}
else if (child.G > G)
{
child.G = G;
child.parent = current;
// 又一次调整堆
openList.add(child);
}
}
}

(7) 回溯法绘制路径

private void drawPath(int[][] maps, Node end)
{
if(end==null||maps==null) return;
System.out.println("总代价:" + end.G);
while (end != null)
{
Coord c = end.coord;
maps[c.y][c.x] = PATH;
end = end.parent;
}
}

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


public void start(MapInfo mapInfo)
{
if(mapInfo==null) return;
// clean
openList.clear();
closeList.clear();
// 開始搜索
openList.add(mapInfo.start);
moveNodes(mapInfo);
} /**
* 移动当前结点
*/
private void moveNodes(MapInfo mapInfo)
{
while (!openList.isEmpty())
{
if (isCoordInClose(mapInfo.end.coord))
{
drawPath(mapInfo.maps, mapInfo.end);
break;
}
Node current = openList.poll();
closeList.add(current);
addNeighborNodeInOpen(mapInfo,current);
}
}

附:源代码地址:点击这里

A星算法(Java实现)的更多相关文章

  1. Java开源-astar:A 星算法

    astar A星算法Java实现 一.适用场景 在一张地图中,绘制从起点移动到终点的最优路径,地图中会有障碍物,必须绕开障碍物. 二.算法思路 1. 回溯法得到路径 (如果有路径)采用“结点与结点的父 ...

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

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

  3. 算法起步之A星算法

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

  4. 归并排序算法 java 实现

    归并排序算法 java 实现 可视化对比十多种排序算法(C#版) [直观学习排序算法] 视觉直观感受若干常用排序算法 算法概念 归并排序是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Di ...

  5. 快速排序算法 java 实现

    快速排序算法 java 实现 快速排序算法Java实现 白话经典算法系列之六 快速排序 快速搞定 各种排序算法的分析及java实现 算法概念 快速排序是C.R.A.Hoare于1962年提出的一种划分 ...

  6. 堆排序算法 java 实现

    堆排序算法 java 实现 白话经典算法系列之七 堆与堆排序 Java排序算法(三):堆排序 算法概念 堆排序(HeapSort)是指利用堆积树(堆)这种数据结构所设计的一种排序算法,可以利用数组的特 ...

  7. Atitit 电子商务订单号码算法(java c# php js 微信

    Atitit 电子商务订单号码算法(java c# php js  微信 1.1. Js版本的居然钱三爷里面没有..只好自己实现了. 1.2. 订单号标准化...长度16位 1.3. 订单号的结构 前 ...

  8. 无向图的最短路径算法JAVA实现

    一,问题描述 给出一个无向图,指定无向图中某个顶点作为源点.求出图中所有顶点到源点的最短路径. 无向图的最短路径其实是源点到该顶点的最少边的数目. 本文假设图的信息保存在文件中,通过读取文件来构造图. ...

  9. POJ 2449 Remmarguts' Date (SPFA + A星算法) - from lanshui_Yang

    题目大意:给你一个有向图,并给你三个数s.t 和 k ,让你求从点 s 到 点 t 的第 k 短的路径.如果第 k 短路不存在,则输出“-1” ,否则,输出第 k 短路的长度. 解题思路:这道题是一道 ...

随机推荐

  1. ANALYZE - 收集与数据库有关的统计

    SYNOPSIS ANALYZE [ VERBOSE ] [ table [ (column [, ...] ) ] ] DESCRIPTION 描述 ANALYZE 收集有关 PostgreSQL ...

  2. 自动交互脚本之expect使用记录

    之前一直没怎么用这个命令,意外用了一下,还不错,那这个是干嘛的呢 我们或多或少会远程登录其他服务器,需要执行某项任务,通常需要手动接入,输入密码啊,等等 那我们如何有效的自动执行呢,expect可以解 ...

  3. 拼字符串 showArray.push(data); showArray.join(",")

    //---// var showColumns = getShowColsRows("table");// var showArray = [];// $.each(showCol ...

  4. 从Excel读取数据,然后分析相似的数据,多线程处理(多线程比较相似的字符串,统计出相似的数量及字符串)

    之前的jar包有问题,现已修改. 需要的jar包,已修改 自己去Maven中央仓库下载jar包. excel数据: 直接上代码. 程序再度优化了一遍.之后如果想再度精准,可能需要建模,最近没空继续做了 ...

  5. js 页面图片等元素在普通元素中滚动动态加载技术

    /*! * 2012-01-13 v1.1 偏移值计算修改 position → offset * 2012-09-25 v1.2 增加滚动容器参数, 回调参数 * 2015-11-17 v1.3 只 ...

  6. CentOS7.4 搭建和使用telnet

    1.先检查是否安装了telnet rpm -qa | grep telnet  //检查你的CentOS是否安装了telnet和telnet-server rpm -qa xinetd //检查你的C ...

  7. linux下的文档处理及tar命令

    1.使用cat命令进行纵向合并 使用‘>’是将左边的内容覆盖到右边 使用‘>>’是将左边的内容追加到右边文档中 还可使用‘>’将不同文件进行合并 2.管道符‘|’统计行数 使用 ...

  8. Spider-Python爬虫之使用Selenium模拟浏览器行为

    分析 他的代码比较简单,主要有以下的步骤:使用BeautifulSoup库,打开百度贴吧的首页地址,再解析得到id为new_list标签底下的img标签,最后将img标签的图片保存下来. header ...

  9. Oracle on Azure

    价格列表 https://azure.microsoft.com/en-us/marketplace/partners/msopentech/oracle-db-12c/ Oracle-Base 安装 ...

  10. Android 笔记一:线性布局

    建立布局 新建项目后,在如图路径下新建xml文件可以开始编辑 weight的使用 android:layout_width="0dp",或android:layout_width= ...