数据结构与算法简记--A*算法

A*算法


 问题

实现游戏地图中自动寻路功能

分析

可使用Dijkstra算法计算最短路径,让游戏角色按路径行走;

但Dijkstra算法是BFS的思想,会有盲目计算,效率低;

寻路不用非得走最短路径,可走次短即可

鉴于平衡效率与效果,使用优化版Dijkstra算法:A*算法

A*算法

优化版Dijkstra算法

启发式搜索

引入启发函数(heuristic function)来优化出队顺序

根据启发函数和Dijkstra算法顶点距离g(i),得到估价函数(evaluation function):f(i)=g(i)+h(i)

此例中的启发函数选择

    • 欧几里得距离:直线距离,但会涉及比较耗时的开根号计算
    • 曼哈顿距离(Manhattan distance):两点之间横纵坐标的距离之和。计算的过程只涉及加减法、符号位反转,所以比欧几里得距离更加高效
    • int hManhattan(Vertex v1, Vertex v2) { // Vertex表示顶点,后面有定义
        return Math.abs(v1.x - v2.x) + Math.abs(v1.y - v2.y);
      }

实现

顶点 Vertex 类的定义,相比Dijkstra 算法中的定义,多了 x,y 坐标,以及估价值 f(i) 。

  • private class Vertex {
      public int id; // 顶点编号ID
      public int dist; // 从起始顶点,到这个顶点的距离,也就是g(i)
      public int f; // 新增:f(i)=g(i)+h(i)
      public int x, y; // 新增:顶点在地图中的坐标(x, y)
      public Vertex(int id, int x, int y) {
        this.id = id;
        this.x = x;
        this.y = y;
        this.f = Integer.MAX_VALUE;
        this.dist = Integer.MAX_VALUE;
      }
    }
    // Graph类的成员变量,在构造函数中初始化
    Vertex[] vertexes = new Vertex[this.v];
    // 新增一个方法,添加顶点的坐标
    public void addVetex(int id, int x, int y) {
      vertexes[id] = new Vertex(id, x, y)
    }

A* 算法是对 Dijkstra 算法的简单改造,实际上稍微改动几行代码,就能把 Dijkstra 算法的代码实现,改成 A* 算法的代码实现,

相比 Dijkstra 算法的代码实现,主要有 3 点区别:

    • 优先级队列构建的方式不同。A* 算法是根据 f 值来构建优先级队列,而 Dijkstra 算法是根据 g 值;
    • A* 算法在更新顶点 dist 值的时候,会同步更新 f 值;
    • 循环结束的条件也不一样。Dijkstra 算法是在终点出队列的时候才结束,A* 算法是一旦遍历到终点就结束。
  • public void astar(int s, int t) { // 从顶点s到顶点t的路径
      int[] predecessor = new int[this.v]; // 用来还原路径
      // 按照vertex的f值构建的小顶堆,而不是按照dist
      PriorityQueue queue = new PriorityQueue(this.v);
      boolean[] inqueue = new boolean[this.v]; // 标记是否进入过队列
      vertexes[s].dist = 0;
      vertexes[s].f = 0;
      queue.add(vertexes[s]);
      inqueue[s] = true;
      while (!queue.isEmpty()) {
        Vertex minVertex = queue.poll(); // 取堆顶元素并删除
        for (int i = 0; i < adj[minVertex.id].size(); ++i) {
          Edge e = adj[minVertex.id].get(i); // 取出一条minVetex相连的边
          Vertex nextVertex = vertexes[e.tid]; // minVertex-->nextVertex
          if (minVertex.dist + e.w < nextVertex.dist) { // 更新next的dist,f
            nextVertex.dist = minVertex.dist + e.w;
            nextVertex.f 
               = nextVertex.dist+hManhattan(nextVertex, vertexes[t]);
            predecessor[nextVertex.id] = minVertex.id;
            if (inqueue[nextVertex.id] == true) {
              queue.update(nextVertex);
            } else {
              queue.add(nextVertex);
              inqueue[nextVertex.id] = true;
            }
          }
          if (nextVertex.id == t) { // 只要到达t就可以结束while了
            queue.clear(); // 清空queue,才能推出while循环
            break; 
          }
        }
      }
      // 输出路径
      System.out.print(s);
      print(s, t, predecessor); // print函数请参看Dijkstra算法的实现
    }

 

 

原文地址:https://www.cnblogs.com/wod-Y/p/12157340.html