A星寻径算法

import java.util.Comparator;

// 节点类
public class Node implements Comparable<Node>, Comparator<Node> {
    /**
     * 节点坐标
     */
    public int x, y;
    /**
     * F(n)=G(n)+H(n)
     */
    public int f;
    /**
     * 起点到当前点的移动耗费
     */
    public int g;
    /**
     * 当前点到终点的移动耗费,即
     * 曼哈顿距离|x1-x2|*水平方向单元格宽度+
     * |y1-y2|*垂直方向单元格宽度(忽略障碍物)
     */
    public int h;
    /**
     * 父节点
     */
    public Node parent;

    /**
     * 通过给定值构造一个节点对象
     */
    public Node(int x, int y, Node parent) {
        this.x = x;
        this.y = y;
        this.parent = parent;
    }

    /**
     * 通过给定节点构造一个新的节点对象
     */
    public Node(Node node) {
        x = node.x;
        y = node.y;
        f = node.f;
        g = node.g;
        h = node.h;
        parent = node.parent;
    }

    /**
     * 将节点重新赋值
     */
    public void reset(Node node) {
        x = node.x;
        y = node.y;
        f = node.f;
        g = node.g;
        h = node.h;
        parent = node.parent;
    }

    @Override
    public int compareTo(Node node) {
        if (f > node.f) return 1;
        else if (f < node.f) return -1;
        else return 0;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null) return false;
        if (!(obj instanceof Node)) return false;
        Node other = (Node) obj;
        if (x != other.x) return false;
        if (y != other.y) return false;
        return true;
    }

    @Override
    public int hashCode() {
        int prime = 31;
        int result = 1;
        result = prime * result + x;
        result = prime * result + y;
        return result;
    }

    @Override
    public String toString() {
        return "(" + x + "," + y + ") F=" + f + "";
    }

    @Override
    public int compare(Node node, Node other) {
        if (node.f > other.f) return 1;
        else if (node.f < other.f) return -1;
        else return 0;
    }

}



import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * A星算法步骤:
 * 1.起点先添加到开启列表中
 * 2.开启列表中有节点的话,取出第一个节点,即最小F值的节点,
 * ->判断此节点是否是目标点,是则找到了,跳出;
 * ->根据此节点取得八个方向的节点,求出G,H,F值;
 * ->判断每个节点在地图中是否能通过,不能通过则加入关闭列表中,跳出;
 * ->判断每个节点是否在关闭列表中,在则跳出;
 * ->判断每个节点是否在开启列表中,在则更新G值,F值,还更新其父节点;不在则将其添加到开启列表中,计算G值,H值,F值,添加其节点。
 * 3.把此节点从开启列表中删除,再添加到关闭列表中;
 * 4.把开启列表中按照F值最小的节点进行排序,最小的F值在第一个;
 * 5.重复2,3,4步骤,直到目标点在开启列表中,即找到了;目标点不在开启列表中,开启列表为空,即没找到
 */
public class AStar {
    /**
     * 地图数组
     */
    private int[][] map;
    /**
     * 障碍物数组
     */
    private int[] limit;
    /**
     * 不可达区域数组
     */
    private boolean[][] close;
    /**
     * 存放未关闭节点的集合
     */
    private List<Node> open;
    /**
     * 结果集
     */
    private List<Node> result;
    /**
     * 垂直或水平方向移动的路径评分
     */
    private final int COST_STRAIGHT = 64;
    /**
     * 斜方向移动的路径评分
     */
    private final int COST_DIAGONAL = 90;
    /**
     * 地图数组的宽度
     */
    private int mapW;
    /**
     * 地图数组的高度
     */
    private int mapH;
    /**
     * 是否能斜向移动
     */
    public boolean isObliqueable;
    /**
     * 是否能直线移动
     */
    public boolean isStraightable;
    /**
     * 起点节点对象
     */
    private Node startNode;
    /**
     * 当前节点对象
     */
    private Node current;
    /**
     * 判断通行的通用节点(另一个作用:Map所有key对应的键 )
     */
    private Node tempNode;
    int count;

    /**
     * @param map   地图数组
     * @param limit 不可通行区域编号的数组
     */
    public AStar(int[][] map, int... limit) {
        tempNode = new Node(0, 0, null);
        startNode = new Node(0, 0, null);
        open = new ArrayList<Node>();
        result = new ArrayList<Node>();
        isObliqueable = true;
        isStraightable = true;
        init(map, limit);
    }

    /**
     * 重新初始化 提高类的复用性
     *
     * @param map   地图数组
     * @param limit 不可通行区域编号的数组
     */
    public AStar init(int[][] map, int... limit) {
        this.map = map;
        this.limit = limit;
        if (close == null || mapW != map[0].length || mapH != map.length) {
            mapW = map[0].length;
            mapH = map.length;
            close = new boolean[mapH][mapW];
        }
        return this;
    }

    /**
     * 程序入口
     * 查找核心算法
     */
    public List<Node> searchPath(int startX, int startY,
                                 int targetX, int targetY) {
        if (startX < 0 || startX >= mapW || targetX < 0 || targetX >= mapW
                || startY < 0 || startY >= mapH || targetY < 0 || targetY >= mapH)
            return null;
        // 查找障碍集合是否存在下一步的坐标
        for (int i = 0, y, x, h = close.length, w = close[0].length, len = limit.length; i < len; i++) {
            /** 将地图数组映射到一个布尔二维数组(可通行为true其他为false) */
            for (y = 0; y < h; y++) {
                for (x = 0; x < w; x++) {
                    if (map[y][x] == limit[i]) {
                        close[y][x] = false;
                    } else {
                        close[y][x] = true;
                    }
                }
            }
        }
        count = 0;
        // 每次调用寻径时 先清空所有集合中的原有数据
        open.clear();
        // 起点先添加到开启列表中
        startNode.x = startX;
        startNode.y = startY;
        open.add(startNode);
        while (!open.isEmpty()) {
            // 开启列表中排序,把F值最低的放到最底端
            Collections.sort(open);
            // 从开启列表中取出并删除第一个节点(F为最低的)
            current = open.remove(0);
            // 判断是否找到目标点
            if (current.x == targetX && current.y == targetY) {
                result.clear();
                // 将终点到起点的路径添加到结果集中
                while (current.parent != null) {
                    result.add(current);
                    current = current.parent;
                }
                return result;
            }
            // 左
            if (isStraightable && current.x - 1 >= 0) {
                createNextStep(current.x - 1, current.y, targetX, targetY,
                        COST_STRAIGHT);
            }
            // 上
            if (isStraightable && current.y - 1 >= 0) {
                createNextStep(current.x, current.y - 1, targetX, targetY,
                        COST_STRAIGHT);
            }
            // 右
            if (isStraightable && current.x + 1 < mapW) {
                createNextStep(current.x + 1, current.y, targetX, targetY,
                        COST_STRAIGHT);
            }
            // 下
            if (isStraightable && current.y + 1 < mapH) {
                createNextStep(current.x, current.y + 1, targetX, targetY,
                        COST_STRAIGHT);
            }
            // 左上
            if (isObliqueable && current.x - 1 >= 0 && current.y - 1 >= 0) {
                createNextStep(current.x - 1, current.y - 1, targetX, targetY,
                        COST_DIAGONAL);
            }
            // 左下
            if (isObliqueable && current.x - 1 >= 0 && current.y + 1 < mapH) {
                createNextStep(current.x - 1, current.y + 1, targetX, targetY,
                        COST_DIAGONAL);
            }
            // 右上
            if (isObliqueable && current.x + 1 < mapW && current.y - 1 >= 0) {
                createNextStep(current.x + 1, current.y - 1, targetX, targetY,
                        COST_DIAGONAL);
            }
            // 右下
            if (isObliqueable && current.x + 1 < mapW && current.y + 1 < mapH) {
                createNextStep(current.x + 1, current.y + 1, targetX, targetY,
                        COST_DIAGONAL);
            }
            // 添加到关闭列表中
            close[current.y][current.x] = false;
        }
        return null;
    }

    /**
     * 根据坐标可否通行创建下一步
     */
    private boolean createNextStep(int x, int y,
                                   int targetX, int targetY, int cost) {
        // 查找关闭集合中是否存在下一步的坐标
        if (!close[y][x]) return false;
        // 查找开启列表中是否存在
        tempNode.x = x;
        tempNode.y = y;
        int index = open.indexOf(tempNode);
        Node node;
        if (index != -1) {
            // G值是否更小,即是否更新G,F值
            if (current.g + cost < open.get(index).g) {
                // 计算G F值
                tempNode.g = current.g + cost;
                tempNode.h = 0;
                tempNode.f = tempNode.g + tempNode.h;
                tempNode.parent = current;
                node = new Node(tempNode);
                // 替换原有节点
                open.set(index, node);
                ++count;
            }
        } else {
            // 计算G H F值
            tempNode.g = current.g + cost;
            tempNode.h = ((tempNode.x > targetX ? tempNode.x - targetX : targetX
                    - tempNode.x) << 6)
                    + ((tempNode.y > targetY ? tempNode.y - targetY : targetY - tempNode.y) << 6);
            tempNode.f = tempNode.g + tempNode.h;
            tempNode.parent = current;
            // 添加到开启列表中
            node = new Node(tempNode);
            open.add(node);
            ++count;
        }
        return true;
    }
}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,651评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,468评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,931评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,218评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,234评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,198评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,084评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,926评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,341评论 1 311
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,563评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,731评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,430评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,036评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,676评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,829评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,743评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,629评论 2 354

推荐阅读更多精彩内容