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

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);
    }
}

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

时间: 2024-10-01 20:01:15

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

A星算法——Java修正版

package com.test; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; public class AStar { private int[][] map;// 地图(1可通过 0不可通过) private List<Node> openList;// 开启列表 private List<Node> c

[算法学习]A星算法

一.适用场景 在一张地图中,绘制从起点移动到终点的最优路径,地图中会有障碍物,必须绕开障碍物. 二.算法思路 1. 回溯法得到路径 (如果有路径)采用"结点与结点的父节点"的关系从最终结点回溯到起点,得到路径. 2. 路径代价的估算:F = G+H A星算法的代价计算使用了被称作是启发式的代价函数. 先说明一下各符号意义:G表示的是 ** 从起点到当前结点的实际路径代价 ** (为啥叫实际?就是已经走过了,边走边将代价计算好了):H表示 ** 当前结点到达最终结点的估计代价 ** (为

银行家算法java实现

关于银行家算法的理论知识,课本或者百度上有好多资料,我就不再多说了,这里把我最近写的银行家算法的实现带码贴出来. 由于这是我们的一个实验,对系统资源数和进程数都指定了,所以这里也将其指定了,其中系统资源数为3,进程数为5. import java.util.Scanner; import javax.swing.plaf.basic.BasicInternalFrameTitlePane.MaximizeAction; import javax.swing.text.StyledEditorKi

无向图的最短路径算法JAVA实现(转)

一,问题描述 给出一个无向图,指定无向图中某个顶点作为源点.求出图中所有顶点到源点的最短路径. 无向图的最短路径其实是源点到该顶点的最少边的数目. 本文假设图的信息保存在文件中,通过读取文件来构造图.文件内容的格式参考这篇文章第一部分. 二,算法实现思路 无向图的最短路径实现相对于带权的有向图最短路径实现要简单得多. 源点的最短路径距离为0,从源点开始,采用广度优先的顺序,首先将与源点邻接的顶点的路径求出,然后再依次求解图中其他顶点的最短路径. 由于顶点的最短路径的求解顺序 是一个 广度优先的顺

排序算法Java实现

排序算法Java实现 排序算法的分类: 内部排序,在排序过程中,全部记录放在内存中,称为内部排序: 外部排序,在排序过程中需要使用外部存储(磁盘),则称为外部排序. 主要介绍内部排序: 插入排序:直接插入排序.二分法插入排序.希尔排序 选择排序:简单选择排序.堆排序 交换排序:冒泡排序.快速排序 归并排序 基数排序 插入排序 直接插入排序 基本思想:对于给定的一组记录,初始时假设第一个记录自成一个有序序列,其余记录为无序序列.接着从第二个记录开始,按照记录的大小依次将当前处理的记录插入到其之前的

A*搜寻算法(A星算法)

A*搜寻算法[编辑] 维基百科,自由的百科全书 本条目需要补充更多来源.(2015年6月30日) 请协助添加多方面可靠来源以改善这篇条目,无法查证的内容可能会被提出异议而移除. A*搜索算法,俗称A星算法.这是一种在图形平面上,有多个节点的路径,求出最低通过成本的算法.常用于游戏中的NPC的移动计算,或在线游戏的BOT的移动计算上. 该算法综合了BFS(Breadth First Search)和Dijkstra算法的优点:在进行启发式搜索提高算法效率的同时,可以保证找到一条最优路径(基于评估函

基于HTML5的WebGL呈现A星算法的3D可视化

http://www.hightopo.com/demo/astar/astar.html 最近搞个游戏遇到最短路径的常规游戏问题,一时起兴基于HT for Web写了个A*算法的WebGL 3D呈现,算法基于开源 https://github.com/bgrins/javascript-astar 的javascript实现,其实作者也有个不错的2D例子实现 http://www.briangrinstead.com/files/astar/ ,只不过觉得所有A*算法的可视化实现都是平面的不够

算法 《秦九韶算法java实践》

[历史背景] 秦九韶算法是中国南宋时期的数学家秦九韶表述求解一元高次多项式的值的算法--正负开方术.它也可以配合牛顿法用来求解一元高次多项式的根.在西方被称作霍纳算法(Horner algorithm或Horner scheme),是以英国数学家威廉·乔治·霍纳命名的. [原理解释] 设有n+1项的n次函数 f(x)=anxn+ an-1xn-1+an-2xn-2+ an-3xn-3+-- a2x2+a1x+ a0 将前n项提取公因子x,得 f(x)=(anxn-1+ an-1xn-2+an-2

Redis rdb文件CRC64校验算法 Java实现

查看RDB文件结构,发现最后的8字节是CRC64校验算得,从文件头开始直到8字节校验码前的FF结束码(含),经过CRC64校验计算发现,貌似最后的8字节是小端模式实现的. 参考redis的crc64实现的代码,点击查看 Java代码如下: 1 package com.jadic.utils; 2 3 /** 4 * @author Jadic 5 * @created 2014-5-15 6 */ 7 public class CRC64 { 8 private static final lon