算法07-A-star

作者: 最爱的火 | 来源:发表于2018-05-27 08:42 被阅读13次

    算法07-A-star

    一、介绍

    A-Star(A*)算法是一种静态路网中求解最短路最有效的方法。它使用估价函数表示走下一个点时的路程,然后从能走的点中选取路程最小的那个点开始走,直到走到终点。估价函数公式表示为:f(n)=g(n)+h(n),其中f(n)是节点n从初始点到目标点的估价函数,g(n)是在状态空间中从初始节点到n节点的实际代价,h(n)是从n到目标节点最佳路径的估计代价。

    A-Star算法分为简化版和完整版。

    A-Star算法的应用:游戏找路。

    二、A-Star简化版

    1、基本思想

    A-Star算法简化版只需把走过的点标记为1,当到达终点时,所有为1的点,即为最短路径。适用于没有凹形障碍物的情形,效率比完整版高。

    操作步骤:

    1. 从起点开始,通过估价函数f(n),判断周围8个点中,使得路径最短的那个点A,将这个点标记为1(代表走过);
    2. 把A当作新的起点,重复步骤1,直到到达终点。

    2、关键代码

    /**
     * @param map  用一个二维数组表示地图。其中0表示走得通,1表示走过,2表示终点,3表示不能走。
     * @param star 起点
     * @param end  终点
     */
    public static void AStarSort(int[][] map, Point star, Point end) {
        if (star == null && end == null) {
            return;
        }
        if (star.x >= map.length - 1 || star.y >= map[0].length - 1) {
            return;
        }
        //到达终点
        if (star.x == end.x && star.y == end.y) {
            return;
        }
        // 标记当前位置已走过
        map[star.x][star.y] = 1;
        //查找下一个能走的点
        Point next = lookup(map, star, end);
        //以next为起点,重新找寻
        AStarSort(map, next, end);
    }
    
    /**
     * 查找下一个要走的点
     *
     * @param map   地图
     * @param point 当前点
     * @param end   终点
     * @return 下一个要走的点
     */
    public static Point lookup(int[][] map, Point point, Point end) {
        //保存所有可走的点
        List<Point> result = new ArrayList<>();
        // 表示1维数组下标
        int x = point.x;
        // 表示2维数组下标
        int y = point.y;
        // 如果左边可以走,就添加到result
        if (map[x - 1][y] != 1 && map[x - 1][y] != 3) {
            result.add(new Point(x - 1, y));
        }
        // 如果上边可以走,就添加到result
        if (map[x][y - 1] != 1 && map[x][y - 1] != 3) {
            result.add(new Point(x, y - 1));
        }
        // 如果右边可以走,就添加到result
        if (map[x + 1][y] != 1 && map[x + 1][y] != 3) {
            result.add(new Point(x + 1, y));
        }
        // 如果下边可以走,就添加到result
        if (map[x][y + 1] != 1 && map[x][y + 1] != 3) {
            result.add(new Point(x, y + 1));
        }
        // 如果左上可以走,就添加到result
        if (map[x - 1][y - 1] != 1 && map[x - 1][y - 1] != 3) {
            result.add(new Point(x - 1, y - 1));
        }
        // 如果右下可以走,就添加到result
        if (map[x + 1][y - 1] != 1 && map[x + 1][y - 1] != 3) {
            result.add(new Point(x + 1, y - 1));
        }
        // 如果左下可以走,就添加到result
        if (map[x - 1][y + 1] != 1 && map[x - 1][y + 1] != 3) {
            result.add(new Point(x - 1, y + 1));
        }
        // 如果右下可以走,就添加到result
        if (map[x + 1][y + 1] != 1 && map[x + 1][y + 1] != 3) {
            result.add(new Point(x + 1, y + 1));
        }
    
        //如果没有可走的点,就退出寻路
        if (result.size() == 0) {
            return null;
        }
    
        int index = 0;
        int dstance = Integer.MAX_VALUE;
        for (int i = 0; i < result.size(); i++) {
            //使用估价函数,估算两点的距离
            int l = getDistance(point, result.get(i), end);
            //找到距离最小的那个点
            if (dstance > l) {
                index = i;
                dstance = l;
            }
        }
        return result.get(index);
    }
    
    /**
     * 估价函数:起点与节点的距离+节点与终点的距离
     *
     * @param start 起点
     * @param node  节点
     * @param end   终点
     * @return 估价的距离
     */
    public static int getDistance(Point start, Point node, Point end) {
        return getDistance(start, node) + getDistance(node, end);
    }
    
    /**
     * 计算两点距离
     *
     * @param start 起点
     * @param end   终点
     * @return 两点距离
     */
    public static int getDistance(Point start, Point end) {
        if (start == null || end == null) {
            return Integer.MAX_VALUE;
        }
        int a = end.x - start.x;
        int b = end.y - start.y;
        int c = (int) Math.sqrt(a * a + b * b);
        return c;
    }
    

    三、A-Star完整版

    1、基本思想

    A-Star算法完整版每次从所有能走的点中选取最小的点A开始走,然后以A为节点动态更新所有能走的点的估价距离,并且保存A的上一个节点。当走到重点时,就可以通过保存的上一个节点,回退实际的最短路线。

    A-Star算法完整版适用于有凹形障碍物的情形。

    操作步骤:

    1. 声明openList保存所有能走的点,声明closeList保存所有走过的点,把起点start添加到openList中
    2. 从openList中最小的点A开始走,把A添加到closeList中,然后重新查找所有可走的点。如果找到的点p之前不在openList中,把它直接添加到openList中;如果p之前就在openList中,判断经过节点A时p的估价L1与走原来方向时p的估价距离L2的大小,如果L1<L2,就改走A,把A设为p的parent节点,把L1设为p的估价距离,然后对openList重新排序;
    3. 重复步骤2,直到到达终点。
    4. 最后,根据终点end的parent,可以回推实际的最短路线。

    2、代码实现

    /**
     * @param mapInfo 地图
     * @return 实际的最短路线
     */
    public static Stack<Node> AStarSort(MapInfo mapInfo) {
        if (mapInfo == null) {
            return null;
        }
        // 使用自动排序队列表示能走的点
        Queue<Node> openList = new PriorityQueue<>();
        // 已经走过的点
        List<Node> closeList = new ArrayList<>();
        openList.add(mapInfo.start);
    
        //如果有路可以走
        while (!openList.isEmpty()) {
            // 到达终点
            if (isColsed(closeList, mapInfo.end)) {
                //展示路线
                Stack<Node> result = drawPath(mapInfo);
                return result;
            }
    
            // 获取最小的节点
            Node now = openList.poll();
            //标记now已走过
            closeList.add(now);
            //寻找下一个可走的点
            addNext(mapInfo, openList, closeList, now);
        }
        return null;
    }
    
    /**
     * 寻找下一个可走的点
     *
     * @param mapInfo   地图
     * @param openList  能走的点
     * @param closeList 走过的点
     * @param now       当前点
     */
    private static void addNext(MapInfo mapInfo, Queue<Node> openList, List<Node> closeList, Node now) {
        //一维数组的下标
        int x = now.p.x;
        //二维数组的下标
        int y = now.p.y;
        addNext(mapInfo, openList, closeList, now, new Point(x - 1, y), DIRECT_VALUE);// 左
        addNext(mapInfo, openList, closeList, now, new Point(x, y - 1), DIRECT_VALUE);// 上
        addNext(mapInfo, openList, closeList, now, new Point(x + 1, y), DIRECT_VALUE);// 右
        addNext(mapInfo, openList, closeList, now, new Point(x, y + 1), DIRECT_VALUE);// 下
        addNext(mapInfo, openList, closeList, now, new Point(x - 1, y - 1), OBLIQUE_VALUE);// 左上
        addNext(mapInfo, openList, closeList, now, new Point(x + 1, y - 1), OBLIQUE_VALUE);// 右上
        addNext(mapInfo, openList, closeList, now, new Point(x - 1, y + 1), OBLIQUE_VALUE);// 左下
        addNext(mapInfo, openList, closeList, now, new Point(x + 1, y + 1), OBLIQUE_VALUE);// 右下
    }
    
    
    /**
     * 寻找下一个可走的点
     *
     * @param mapInfo   地图
     * @param openList  能走的点
     * @param closeList 走过的点
     * @param now       当前点
     * @param p         下一个准备走的点
     * @param value     从now到p的距离
     */
    private static void addNext(MapInfo mapInfo, Queue<Node> openList, List<Node> closeList, Node now, Point p, int value) {
        //先判断当前点能否添加到openList中:如果已经走过或者是障碍物,就不添加到openList中
        if (canAddOpen(mapInfo, closeList, p)) {
            Node end = mapInfo.end;
            // 从起点到p的距离
            int g = now.g + value;
            //从openList找p
            Node child = findOpen(openList, p);
            //如果是第一次遇到这个点
            if (child == null) {
                //p到终点的距离
                int h = calcH(end.p, p) * DIRECT_VALUE;
                //如果到达终点,就更新end的值,然后添加到openList中
                if (p.equals(end.p)) {
                    child = end;
                    //设置parent是为从end节点推路线图
                    child.parent = now;
                    child.g = g;
                    child.h = h;
                    //如果不是终点,就新建一个对象
                } else {
                    child = new Node(p, now, g, h);
                }
                //添加到openList中
                openList.add(child);
                //如果p已经在openList中,走当前路线的距离小于原来路线的距离,就用当前的g值替换原来的g值
            } else if (child.g > g) {
                child.g = g;
                //从now节点走,比原来的路线近,所以将它的parent设为now
                child.parent = now;
                // 重新排序,以便选择距离最小的点
                openList.add(child);
            }
        }
    }
    

    最后

    代码地址:https://gitee.com/yanhuo2008/Common/tree/master/Tool/src/main/java/gsw/tool/arithmetic/astar

    数据结构与算法专题:https://www.jianshu.com/nb/25128590

    喜欢请点赞,谢谢!

    相关文章

      网友评论

        本文标题:算法07-A-star

        本文链接:https://www.haomeiwen.com/subject/rbtbjftx.html