A星算法的JAVA实现

A星算法
A*搜寻算法,俗称A星算法。这是一种在图形平面上,有多个节点的路径,求出最低通过成本的算法。常用于游戏中的NPC的移动计算,或线上游戏的BOT的移动计算上。

该算法像Dijkstra算法一样,可以找到一条最短路径;也像BFS一样,进行启发式的搜索。

 

 Dijkstra的Java实现可以参照《Java实现Dijkstra算法》。

 

具体A星算法的理论部分已经在《A星算法——理论篇》一文中有详细说明,现直接上代码部分,该代码借助A星算法解决了一道acm题。题目如下:

 

援救行动
Problem
Angel被传说中神秘的邪恶的Moligpy人抓住了!他被关在一个迷宫中。迷宫的长、宽不超过200。

迷宫中有不可以越过的墙以及监狱的看守。

Angel的朋友带了一些救援队来到了迷宫中。他们的任务是:接近Angel。我们假设接近Angel就是到达Angel所在的位置。

假设移动需要1单位时间,杀死一个看守也需要1单位时间。到达一个格子以后,如果该格子有看守,则一定要杀死(否则会死

的很难看的……只见那个看守开了9倍狙镜……)。交给你的任务是,最少要多少单位时间,才能到达Angel所在的地方?

(只能向上、下、左、右4个方向移动)

Input
该题含有多组测试数据。

每组测试数据第一行二个整数n,m。表示迷宫的大小为n*m。

以后n行,每行m个时字符。其中“#”代表墙,“.”表示可以移动,“x”表示看守,“a”表示Angel,“r”表示救援队伍。

字母均为小写。

Output
一行,代表救出Angel的最短时间。

如果救援小组永远不能达到Angel处,则输出“Poor ANGEL has to stay in the prison all his life.”

Sample Input
7 8
#.#####.
#.a#..r.
#..#x...
..#..#.#
#...##..
.#......
........

Sample Output
13

 

    AStar类:

Java代码   收藏代码
  1. package com.sabrina.astar;  
  2.   
  3. import java.util.Iterator;  
  4. import java.util.LinkedList;  
  5. import java.util.Queue;  
  6.   
  7. public class AStar {  
  8.     // 迷宫图  
  9.     Point[][] maze;  
  10.     // 起始节点  
  11.     Point start;  
  12.     // 终止节点  
  13.     Point goal;  
  14.   
  15.     // 开启队列,用于存放待处理的节点  
  16.     Queue<Point> openQueue = null;  
  17.     // 关闭队列,用于存放已经处理过的节点  
  18.     Queue<Point> closedQueue = null;  
  19.   
  20.     // 起始节点到某个节点的距离  
  21.     int[][] FList = null;  
  22.     // 某个节点到目的节点的距离  
  23.     int[][] GList = null;  
  24.     // 起始节点经过某个节点到目的节点的距离  
  25.     int[][] HList = null;  
  26.   
  27.     /** 
  28.      * 打印行走路径 
  29.      *  
  30.      *  经过的点用'*'表示, 
  31.      *  未经过的点用'.'表示, 
  32.      *  起始节点用'r'表示, 
  33.      *  目的节点用'a'表示 
  34.      *  士兵用'x'表示 
  35.      */  
  36.     public void printPath() {  
  37.         System.out.println("================ printPath ================");  
  38.         Point father_point = null;  
  39.         char[][] result = new char[7][8];  
  40.         for (int i = 0; i < 7; i++) {  
  41.             for (int j = 0; j < 8; j++) {  
  42.                 result[i][j] = '.';  
  43.             }  
  44.         }  
  45.   
  46.         int step = 0;  
  47.         father_point = maze[goal.getX()][goal.getY()];  
  48.         while (father_point != null) {  
  49.             if(father_point.equals(start))  
  50.                 result[father_point.getX()][father_point.getY()] = 'r';  
  51.             else if(father_point.equals(goal)) {  
  52.                 result[father_point.getX()][father_point.getY()] = 'a';  
  53.                 step++;  
  54.             }  
  55.             else if(father_point.getValue() == 'x') {  
  56.                 result[father_point.getX()][father_point.getY()] = 'x';  
  57.                 step += 2;  
  58.             }  
  59.             else {  
  60.                 result[father_point.getX()][father_point.getY()] = '*';  
  61.                 step++;  
  62.             }  
  63.             father_point = father_point.getFather();  
  64.         }  
  65.         // 打印行走步数  
  66.         System.out.println("step is : " + step);  
  67.         for (int i = 0; i < 7; i++) {  
  68.             for (int j = 0; j < 8; j++) {  
  69.                 System.out.print(result[i][j] + " ");  
  70.             }  
  71.             System.out.println();  
  72.         }  
  73.     }  
  74.   
  75.     /** 
  76.      * 构造函数 
  77.      *  
  78.      * @param maze  迷宫图 
  79.      * @param start 起始节点 
  80.      * @param goal  目的节点 
  81.      */  
  82.     public AStar(Point[][] maze, Point start, Point goal) {  
  83.         this.maze = maze;  
  84.         this.start = start;  
  85.         this.goal = goal;  
  86.   
  87.         openQueue = new LinkedList<Point>();  
  88.         closedQueue = new LinkedList<Point>();  
  89.   
  90.         FList = new int[maze.length][maze[0].length];  
  91.         GList = new int[maze.length][maze[0].length];  
  92.         HList = new int[maze.length][maze[0].length];  
  93.   
  94.         for (int i = 0; i < maze.length; i++) {  
  95.             for (int j = 0; j < maze[0].length; j++) {  
  96.                 FList[i][j] = Integer.MAX_VALUE;  
  97.                 GList[i][j] = Integer.MAX_VALUE;  
  98.                 HList[i][j] = Integer.MAX_VALUE;  
  99.             }  
  100.         }  
  101.   
  102.         init();  
  103.     }  
  104.   
  105.     /* 
  106.      * 初始化 
  107.      *  
  108.      *  将起始节点添加至开启列表,初始化: 
  109.      *  1) 起始节点到当前节点(起始节点)的距离 
  110.      *  2) 当前节点(起始节点)到目的节点的距离 
  111.      *  3) 起始节点经过当前节点(起始节点)到目的节点的距离 
  112.      */  
  113.     private void init() {  
  114.         openQueue.offer(start);  
  115.         int start_x = start.getX();  
  116.         int start_y = start.getY();  
  117.         int goal_x = goal.getX();  
  118.         int goal_y = goal.getY();  
  119.   
  120.         // 起始节点到当前节点的距离  
  121.         GList[start_x][start_y] = 0;  
  122.         // 当前节点到目的节点的距离  
  123.         HList[start_x][start_y] = getDistance(start_x, start_y, goal_x, goal_y);  
  124.         // f(x) = g(x) + h(x)  
  125.         FList[start_x][start_y] = GList[start_x][start_y]  
  126.                 + HList[start_x][start_y];  
  127.     }  
  128.   
  129.     /** 
  130.      * 启动搜索迷宫过程主入口 
  131.      *  
  132.      *   从开启列表中搜索F值最小(即:起始节点 经过某一节点 到目的节点 距离最短), 
  133.      *   将选取的节点作为当前节点,并更新当前节点的邻居节点信息(G、H、F值)以及 
  134.      *   开启列表与关闭列表的成员。    
  135.      */  
  136.     public void start() {  
  137.         Point currentPoint;  
  138.   
  139.         while ((currentPoint = findShortestFPoint()) != null) {  
  140.             if (currentPoint.getX() == goal.getX()  
  141.                     && currentPoint.getY() == goal.getY())  
  142.                 return;  
  143.             updateNeighborPoints(currentPoint);  
  144.         }  
  145.     }  
  146.   
  147.     public static void main(String[] args) {  
  148.   
  149.         // 原始迷宫图  
  150.         char[][] mazeRaw = { { '#''.''#''#''#''#''#''.' },  
  151.                              { '#''.''a''#''.''.''r''.' },  
  152.                              { '#''.''.''#''x''.''.''.' },  
  153.                              { '.''.''#''.''.''#''.''#' },  
  154.                              { '#''.''.''.''#''#''.''.' },  
  155.                              { '.''#''.''.''.''.''.''.' },  
  156.                              { '.''.''.''.''.''.''.''.' } };  
  157.   
  158.         // 节点迷宫图  
  159.         Point[][] maze = new Point[mazeRaw.length][mazeRaw[0].length];  
  160.         for (int i = 0; i < maze.length; i++) {  
  161.             for (int j = 0; j < maze[0].length; j++) {  
  162.                 maze[i][j] = new Point(i, j, mazeRaw[i][j]);  
  163.             }  
  164.         }  
  165.         // 起始节点  
  166.         Point start = maze[1][6];  
  167.         // 目的节点  
  168.         Point goal = maze[1][2];  
  169.   
  170.         AStar astar = new AStar(maze, start, goal);  
  171.         // 启动搜索迷宫过程  
  172.         astar.start();  
  173.         // 打印行驶路径  
  174.         astar.printPath();  
  175.   
  176.     }  
  177.   
  178.     /* 
  179.      * 检查位置是否有效 
  180.      *  
  181.      *   如果当前位置存在、不是墙,且不在关闭列表中,则返回"true",表示为有效位置; 
  182.      *   否则,返回"false"。 
  183.      *  
  184.      * 输入: 待检查位置的横坐标值 
  185.      *       待检查位置的纵坐标值 
  186.      *        
  187.      * 输出: 是否有效 
  188.      */  
  189.     private boolean checkPosValid(int x, int y) {  
  190.         // 检查x,y是否越界, 并且当前节点不是墙  
  191.         if ((x >= 0 && x < maze.length) && (y >= 0 && y < maze[0].length)  
  192.                 && (maze[x][y].getValue() != '#')) {  
  193.             // 检查当前节点是否已在关闭队列中,若存在,则返回 "false"  
  194.             Iterator<Point> it = closedQueue.iterator();  
  195.             Point point = null;  
  196.             while (it.hasNext()) {  
  197.                 if ((point = it.next()) != null) {  
  198.                     if (point.getX() == x && point.getY() == y)  
  199.                         return false;  
  200.                 }  
  201.             }  
  202.             return true;  
  203.         }  
  204.         return false;  
  205.     }  
  206.   
  207.     /* 
  208.      * 获取当前位置到目的位置的距离 
  209.      *  
  210.      *   距离衡量规则: 横向移动一格或纵向移动一格的距离为1. 
  211.      *  
  212.      * 输入: 当前位置的横坐标值 
  213.      *       当前位置的纵坐标值 
  214.      *       目的位置的横坐标值 
  215.      *       目的位置的纵坐标值 
  216.      *        
  217.      * 输出: 当前位置到目的位置的距离 
  218.      */  
  219.     private int getDistance(int current_x, int current_y, int goal_x, int goal_y) {  
  220.         return Math.abs(current_x - goal.getX())  
  221.                 + Math.abs(current_y - goal.getY());  
  222.     }  
  223.   
  224.     /* 
  225.      * 找寻最短路径所经过的节点 
  226.      *  
  227.      *   从开启列表中找寻F值最小的节点,将其从开启列表中移除,并置入关闭列表。 
  228.      *  
  229.      * 输出:最短路径所经过的节点 
  230.      */  
  231.     private Point findShortestFPoint() {  
  232.         Point currentPoint = null;  
  233.         Point shortestFPoint = null;  
  234.         int shortestFValue = Integer.MAX_VALUE;  
  235.   
  236.         Iterator<Point> it = openQueue.iterator();  
  237.         while (it.hasNext()) {  
  238.             currentPoint = it.next();  
  239.             if (FList[currentPoint.getX()][currentPoint.getY()] <= shortestFValue) {  
  240.                 shortestFPoint = currentPoint;  
  241.                 shortestFValue = FList[currentPoint.getX()][currentPoint.getY()];  
  242.             }  
  243.         }  
  244.   
  245.         if (shortestFValue != Integer.MAX_VALUE) {  
  246.             openQueue.remove(shortestFPoint);  
  247.             closedQueue.offer(shortestFPoint);  
  248.         }  
  249.   
  250.         return shortestFPoint;  
  251.     }  
  252.   
  253.     /* 
  254.      * 更新邻居节点 
  255.      *  
  256.      *   依次判断上、下、左、右方向的邻居节点,如果邻居节点有效,则更新距离矢量表。 
  257.      *  
  258.      * 输入: 当前节点 
  259.      */  
  260.     private void updateNeighborPoints(Point currentPoint) {  
  261.         int current_x = currentPoint.getX();  
  262.         int current_y = currentPoint.getY();  
  263.   
  264.         // 上  
  265.         if (checkPosValid(current_x - 1, current_y)) {  
  266.             updatePoint(maze[current_x][current_y],  
  267.                     maze[current_x - 1][current_y]);  
  268.         }  
  269.         // 下  
  270.         if (checkPosValid(current_x + 1, current_y)) {  
  271.             updatePoint(maze[current_x][current_y],  
  272.                     maze[current_x + 1][current_y]);  
  273.         }  
  274.         // 左  
  275.         if (checkPosValid(current_x, current_y - 1)) {  
  276.             updatePoint(maze[current_x][current_y],  
  277.                     maze[current_x][current_y - 1]);  
  278.         }  
  279.         // 右  
  280.         if (checkPosValid(current_x, current_y + 1)) {  
  281.             updatePoint(maze[current_x][current_y],  
  282.                     maze[current_x][current_y + 1]);  
  283.         }  
  284.     }  
  285.   
  286.     /* 
  287.      * 更新节点 
  288.      *  
  289.      *   依次计算:1) 起始节点到当前节点的距离; 2) 当前节点到目的位置的距离; 3) 起始节点经过当前节点到目的位置的距离 
  290.      *   如果当前节点在开启列表中不存在,则:置入开启列表,并且“设置”1)/2)/3)值; 
  291.      *   否则,判断 从起始节点、经过上一节点到当前节点、至目的地的距离 < 上一次记录的从起始节点、到当前节点、至目的地的距离, 
  292.      *   如果有更短路径,则更新1)/2)/3)值 
  293.      *  
  294.      * 输入: 上一跳节点(又:父节点) 
  295.      *       当前节点 
  296.      */  
  297.     private void updatePoint(Point lastPoint, Point currentPoint) {  
  298.         int last_x = lastPoint.getX();  
  299.         int last_y = lastPoint.getY();  
  300.         int current_x = currentPoint.getX();  
  301.         int current_y = currentPoint.getY();  
  302.   
  303.         // 起始节点到当前节点的距离  
  304.         int temp_g = GList[last_x][last_y] + 1;  
  305.         if (maze[current_x][current_y].getValue() == 'x'// 如果当前节点是看守  
  306.             ++temp_g;  
  307.         // 当前节点到目的位置的距离  
  308.         int temp_h = getDistance(current_x, current_y, goal.getX(), goal.getY());  
  309.         // f(x) = g(x) + h(x)  
  310.         int temp_f = temp_g + temp_h;  
  311.   
  312.         // 如果当前节点在开启列表中不存在,则:置入开启列表,并且“设置”  
  313.         // 1) 起始节点到当前节点距离  
  314.         // 2) 当前节点到目的节点的距离  
  315.         // 3) 起始节点到目的节点距离  
  316.         if (!openQueue.contains(currentPoint)) {  
  317.             openQueue.offer(currentPoint);  
  318.             currentPoint.setFather(lastPoint);  
  319.   
  320.             // 起始节点到当前节点的距离  
  321.             GList[current_x][current_y] = temp_g;  
  322.             // 当前节点到目的节点的距离  
  323.             HList[current_x][current_y] = temp_h;  
  324.             // f(x) = g(x) + h(x)  
  325.             FList[current_x][current_y] = temp_f;  
  326.         } else {  
  327.   
  328.             // 如果当前节点在开启列表中存在,并且,  
  329.             // 从起始节点、经过上一节点到当前节点、至目的地的距离 < 上一次记录的从起始节点、到当前节点、至目的地的距离,  
  330.             // 则:“更新”  
  331.             // 1) 起始节点到当前节点距离  
  332.             // 2) 当前节点到目的节点的距离  
  333.             // 3) 起始节点到目的节点距离  
  334.             if (temp_f < FList[current_x][current_y]) {  
  335.                 // 起始节点到当前节点的距离  
  336.                 GList[current_x][current_y] = temp_g;  
  337.                 // 当前节点到目的位置的距离  
  338.                 HList[current_x][current_y] = temp_h;  
  339.                 // f(x) = g(x) + h(x)  
  340.                 FList[current_x][current_y] = temp_f;  
  341.                 // 更新当前节点的父节点  
  342.                 currentPoint.setFather(lastPoint);  
  343.             }  
  344.         }  
  345.     }  
  346. }  

 

   Point类:

Java代码   收藏代码
  1. package com.sabrina.astar;  
  2.   
  3. public class Point {  
  4.     // 节点横坐标  
  5.     private int x;  
  6.     // 节点纵坐标  
  7.     private int y;  
  8.   
  9.     // 节点值  
  10.     private char value;  
  11.     // 父节点  
  12.     private Point father;  
  13.   
  14.     /** 
  15.      * 构造函数 
  16.      *  
  17.      * @param x  节点横坐标 
  18.      * @param y  节点纵坐标 
  19.      */  
  20.     public Point(int x, int y) {  
  21.         this.x = x;  
  22.         this.y = y;  
  23.     }  
  24.   
  25.     /** 
  26.      * 构造函数 
  27.      *  
  28.      * @param x      节点横坐标 
  29.      * @param y      节点纵坐标 
  30.      * @param value  节点值 
  31.      */  
  32.     public Point(int x, int y, char value) {  
  33.         this.x = x;  
  34.         this.y = y;  
  35.         this.value = value;  
  36.     }  
  37.       
  38.     public Point getFather() {  
  39.         return father;  
  40.     }  
  41.   
  42.     public void setFather(Point father) {  
  43.         this.father = father;  
  44.     }  
  45.       
  46.     public char getValue() {  
  47.         return value;  
  48.     }  
  49.   
  50.     public int getX() {  
  51.         return x;  
  52.     }  
  53.   
  54.     public int getY() {  
  55.         return y;  
  56.     }  
  57. }  

 

代码执行结果
================ printPath ================
step is : 13
. . . . . . . . 
. . a . * * r . 
. * * . x . . . 
. * . * * . . . 
. * * * . . . . 
. . . . . . . . 
. . . . . . . .

http://joshuasabrina.iteye.com/blog/1811065

https://blog.csdn.net/u011351840/article/details/9280705

猜你喜欢

转载自blog.csdn.net/fengbin2005/article/details/80596903