A*搜索算法

参考:
A星算法详解(个人认为最详细,最通俗易懂的一个版本)
最短路径A*算法原理及java代码实现(看不懂是我的失败)

算法简介

A*搜索算法结合了基于广度搜索的Dijkstra算法和贪心思想的BFS最佳优先搜索的优点设计的最短路径算法,他可以用于含有障碍物的图算法之中。其思想是首先将节点的邻近节点加入带处理队列之中,但是并不是依次处理,而是通过一个评估函数,依据该函数有优先级地对待处理队列中的节点进行处理。这样就能较为快速的找到终点。

算法思想

首先要将节点分类,分为可通行节点,不可通行节点,起始节点和终点。然后节点应该还含有的参数是父节点的指针。
从起点开始,首先搜寻与起点相邻的节点,将其加入待检查队列openlist中。然后进入循环,从openList中取出一个点,取出原则后面介绍,将该节点加入closeList中。由于当前只有起点,因此取出的就是起点。然后将与起点相连的点(不加入路障点和在closeList中的点)加入openList中,并且在加入过程中,要计算每个点的评估函数值F,该值是作为前面提到的从openList中取点原则的依据(取F最小的点)。F=G+H,其中G为从起始节点到该点所要花费的总的代价,H为该点到终点的预测代价。在加入点到openList的过程中,如果该点的父节点为空,则还要将加入点的父节点指定为当前节点;若父节点不为空(说明带加入点已经在openList之中了,此时要做到就是是否更新带加入点的状态),则要比较从当前节点到带加入点的G值还是带加入点的已有G值大,若前者大,则将更新G值和带加入点的父节点,否则不改变带加入点的状态。循环以上操作直至终点加入到openList之中。最后,根据终点的父节点指针,依次找到起始节点。

算法伪代码

main(){
    openList.add(startNode)
    while(!openList.isEmpty()){
        node = openList.getMinFNode();      //opt1
        closeList.add(node);
        if(node==endNode){
            isFind = true;
            break;
         }
       addAdjacentNode(node);                 //opt2
    }
    getTrace();
}
addAdjacentNode(curNode){
    nodeList = getAdjacentNode(curNode);        //opt3
    for(node belong nodeList){
        calculate of update F of node;                     //opt4
        openList.add(node);                                     //opt5
    }
}

优化

opt1

getMinFNode方法可以将openList采用优先队列的方式达到快速得到最小F的点。但是优先队列存在一个问题,就是无法快速的在opt5处更新已经在openList中的节点,因此要配合HashSet使用,所有该数据结构最好自己实现。

opt2

将邻居节点插入openList中时要做一系列工作,比如寻找得到临近节点列表,计算每个临近节点的f值(包含计算H和计算/更新G),将节点加入openList。

opt3

在下面具体实现的代码中,我们采取的节点为方块形式,因此可以采用上下左右斜对角的8个方向进行查找,保证不会遗漏。但是如果对于树形结构的图,也可以采用遍历该节点的邻接链表方式,得到一系列相邻节点。

opt4

F值的计算主要是计算G(G值的计算主要是根据Dijkstra算法思想),如果该节点不在openList中,则直接计算G值并更新父节点;如果该节点在openList中,那么之前肯定计算过G值,此时就是要计算从上一节点lastNode到该节点的G值,并与原先的G值比较,如果新的G值小,则要跟新G值和将该点的父节点重新定向到lastNode上。在我们下面的实例中,G值默认为水平垂直花费为10,斜对角为14。但如果采用的是树形图,则要根据节点之间的权重进行计算(Dijkstra算法)。
H值的计算采用了最佳优先搜索的方法,在下面的实例中,我们采用的是忽略路障的曼哈顿距离来估计,当然也可以依据情况采用欧式距离,非欧距离等方式计算。对于树形图的H计算我暂时也不清楚,这也是我不打算将A星搜索算法用于树形图的原因(节点之间的连接数不确定)

opt5

如opt1中所述,要想用优先队列,就要解决如何在加入时保证加入点不重复,且能及时更新最小F的问题。这也是我没采用优先队列而使用了HashSet的原因(虽然在opt1中要遍历获得最小值f比较慢)

算法实现

Node.java

public class Node {
    int f;
    int g;
    int h;
    Node father;
    Node lastNode;

    int x;
    int y;

    boolean isBarrier;         //可以采用枚举类
    boolean isStart;
    boolean isEnd;
    boolean isTrace;

    public Node() {

    }

    public Node(int i, int j) {
        x = i;
        y = j;
    }

    public static Node getMaxFNode() {
        Node node = new Node();
        node.f = Integer.MAX_VALUE;
        return node;
    }

    @Override
    public String toString() {             //保证HashSet工作

        if(isBarrier)
            return "#";
        else if(isStart)
            return "@";
        else if(isEnd)
            return "$";
        else if(isTrace)
            return "o";
        else 
            return "*";
    }

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

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

}

AStar.java

import java.util.ArrayList;
import java.util.HashSet;

public class AStar {
    private Node[][] map;
    private int xBound;
    private int yBound;
    private Node startNode;
    private Node endNode;
    private HashSet openList;
    private ArrayList closeList;

    public AStar(Node[][] map, Node sNode, Node eNode) {
        this.map = map;
        xBound = map.length-1;
        yBound = map[0].length-1;
        startNode = sNode;
        endNode = eNode;
        openList = new HashSet<>();
        closeList = new ArrayList<>();
    }

    public boolean find() {
        boolean isFind = false;
        openList.add(startNode);
        while(!openList.isEmpty()) {
            Node fNode = Node.getMaxFNode();                    // the node of minimum f
            for(Node node : openList) {
                fNode = fNode.fif(fNode.isEnd) {
                isFind = true;
                break;
            }   
        }
        setTrace();
        return isFind;
    }

    private void checkAdjacent(Node curNode) {
        ArrayList nodeList = getAdjacentNode(curNode);

        for(Node node : nodeList) {
            setG(node);
            setH(node);
            node.f = node.g + node.h;
            openList.add(node);
        }
    }

    private ArrayList getAdjacentNode(Node curNode) {
        ArrayList nodeList = new ArrayList<>();

        int x = curNode.x;
        int y = curNode.y;

        for(int i=-1; i<=1; i++) {
            for(int j=-1; j<=1; j++) {
                if(i==0 && j==0)
                    continue;
                if(x+i>=0 && x+i<=xBound && y+j>=0 && y+j<=yBound
                        && map[x+i][y+j].isBarrier!=true && map[x+i][y+j].isStart!=true
                        && closeList.contains(map[x+i][y+j])!=true) {
                    Node node = map[x+i][y+j];
                    node.lastNode = curNode;
                    nodeList.add(node);
                }
            }
        }
        return nodeList;
    }

    private void setG(Node node) {
        int deltaG;
        if(Math.abs(node.lastNode.x-node.x)==1
                && Math.abs(node.lastNode.y-node.y)==1)
            deltaG = 14;
        else
            deltaG = 10;

        int tempG = node.lastNode.g + deltaG;
        if(node.g==0 || node.g > tempG) {
            node.g = tempG;
            node.father = node.lastNode;
        }
    }

    private void setH(Node node) {
        if(node.h==0)
            node.h = 10*(Math.abs(node.lastNode.x-node.x)+Math.abs(node.lastNode.y-node.y));
    }

    private void setTrace() {
        Node node = endNode;
        while(node!=startNode) {
            node.isTrace = true;
            node = node.father;
        }
    }

}

Test.java

public class Test {
    Node[][] map;
    Node startNode;
    Node endNode;

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Test test = new Test();
        test.print();

        AStar aStar = new AStar(test.map, test.startNode, test.endNode);
        if(aStar.find()) {
            System.out.println("I find it!");
            test.print();
        }
        else {
            System.out.println("failure!");
        }

    }

    public Test() {
        int row = 10;
        int rank = 10;
        map = new Node[row][];
        for(int i=0; inew Node[rank];
            for(int j=0; j0].length; j++) {
                map[i][j] = new Node(i, j);
            }
        }
        map[5][4].isStart = true;
        startNode = map[5][4];
        map[6][6].isEnd = true;
        endNode = map[6][6];
        for(int i=3; i<=7; i++)
            map[i][5].isBarrier = true;
        map[7][3].isBarrier = true;
        map[7][4].isBarrier = true;
        map[3][4].isBarrier = true;
    }

    public void print() {
        for(Node[] row : map) {
            for(Node node : row) {
                System.out.print(node + " ");
            }
            System.out.println();
        }
    }

}

你可能感兴趣的:(search)