当前位置:   article > 正文

A* (A Star) 算法 java实现_java astar算法

java astar算法

A*算法(A-star algorithm)是一种常用的启发式搜索算法,用于求解图中两个节点之间的最短路径。A算法综合了Dijkstra算法的广度优先搜索和贪婪最优搜索策略,通过使用估计函数(heuristic function)来指导搜索方向,从而提高搜索效率。

以下是A*算法的详细步骤:

  1. 初始化:将起始节点添加到一个开放列表(open list)中,并将其估计函数值(f值)设置为0。
  2. 循环直到开放列表为空或找到目标节点:
    a. 从开放列表中选择具有最小估计函数值(f值)的节点,将其称为当前节点,并将其移出开放列表。
    b. 如果当前节点是目标节点,则搜索完成,构建最短路径。
    c. 否则,对于当前节点的每个邻居节点:
    • 如果邻居节点不可通过或已在关闭列表中,则忽略它。
    • 如果邻居节点不在开放列表中,则将其添加到开放列表,并计算它的估计函数值(f值)和代价函数值(g值)。
    • 如果邻居节点已经在开放列表中,并且通过当前节点到达它的代价函数值(g值)更小,则更新它的父节点和代价函数值(g值)。
  3. 如果开放列表为空且未找到目标节点,则表示无法到达目标节点,搜索失败。

A*算法使用了一个估计函数来指导搜索,在每次选择下一个节点时,它考虑了两个值:g值和h值。

  • g值表示从起始节点到当前节点的实际代价。
  • h值表示从当前节点到目标节点的估计代价。

A*算法的估计函数f值定义为:f(n) = g(n) + h(n),其中n是当前节点。f值越小,越有可能是优先考虑的节点。

A*算法的性能和效果取决于所选择的估计函数。一个好的估计函数应该能够提供较为准确的估计值,以便更好地引导搜索,但也需要具备一定的效率,以免过多地增加计算复杂度。

A算法的优点是在适当的估计函数下,能够找到最短路径,并且相对于其他完全搜索算法,它的搜索效率更高。然而,A算法并不能保证一定找到最短路径,且在某些情况下可能会陷入局部最优解。

节点代码

  1. class Node {
  2. int x; // 节点的x坐标
  3. int y; // 节点的y坐标
  4. int g; // 从起始节点到当前节点的实际代价
  5. int h; // 从当前节点到目标节点的估计代价
  6. int f; // 估计函数值
  7. Node parent; // 父节点
  8. public Node(int x, int y) {
  9. this.x = x;
  10. this.y = y;
  11. }
  12. // 必须重写equals方法
  13. @Override
  14. public boolean equals(Object o) {
  15. if (this == o) return true;
  16. if (o == null || getClass() != o.getClass()) return false;
  17. Node node = (Node) o;
  18. return x == node.x && y == node.y;
  19. }
  20. @Override
  21. public int hashCode() {
  22. return Objects.hash(x, y);
  23. }
  24. }

算法实现

  1. public class AStar {
  2. public static void main(String[] args) {
  3. int[][] grid = {
  4. {0, 0, 0, 0, 0},
  5. {1, 1, 0, 1, 0},
  6. {0, 0, 0, 0, 0},
  7. {0, 1, 1, 1, 1},
  8. {0, 0, 0, 0, 0}
  9. };
  10. Node start = new Node(0, 0);
  11. Node end = new Node(4, 4);
  12. List<Node> path = findShortestPath(grid, start, end);
  13. if (path != null) {
  14. for (Node node : path) {
  15. System.out.print("(" + node.x + ", " + node.y + ")");
  16. System.out.print("-> ");
  17. }
  18. System.out.print("end");
  19. } else {
  20. System.out.println("No path found");
  21. }
  22. }
  23. public static List<Node> findShortestPath(int[][] grid, Node start, Node end) {
  24. int rows = grid.length;
  25. int cols = grid[0].length;
  26. //创建开放 关闭列表
  27. List<Node> openList = new ArrayList<>();
  28. List<Node> closeList = new ArrayList<>();
  29. //初始化开始节点
  30. start.g = 0;// 从起始节点到当前节点的实际代价
  31. start.h = calculateHeuristic(start, end);// 从当前节点到目标节点的估计代价
  32. start.f = start.g + start.h;
  33. //添加进开放列表
  34. openList.add(start);
  35. //开放列表不为null就一直循环
  36. while (!openList.isEmpty()) {
  37. //从开放列表中选f最小的节点
  38. Node current = getMinimumFValueNode(openList);
  39. //将当前节点从开放列表中删除
  40. openList.remove(current);
  41. //加入关闭列表
  42. closeList.add(current);
  43. if (current.equals(end)) {
  44. //找到最短路径,回溯到起始节点
  45. return buildPath(current);
  46. }
  47. //当前节点的邻居节点
  48. List<Node> neighbors = getNeighbors(current, grid, rows, cols);
  49. for (Node neighbor : neighbors) {
  50. if(closeList.contains(neighbor)){
  51. //已访问
  52. continue;
  53. }
  54. //当前节点移动到邻居节点 实际代价为1
  55. int tentativeG = current.g+1;
  56. // 如果开放列表中没有该几点,或者 存在该几点但是之走前路径的代价较大
  57. if(!openList.contains(neighbor)||tentativeG<neighbor.g){
  58. neighbor.parent = current;
  59. neighbor.g = tentativeG;
  60. neighbor.h = calculateHeuristic(neighbor,end);
  61. neighbor.f=neighbor.g+neighbor.h;
  62. //已经在里面 就不要添加了
  63. if(!openList.contains(neighbor)){
  64. openList.add(neighbor);
  65. }
  66. }
  67. }
  68. }
  69. return null; //无法找到最短路径
  70. }
  71. // 计算预估代价
  72. private static int calculateHeuristic(Node current, Node end) {
  73. // 使用曼哈顿距离作为估计函数
  74. return Math.abs(current.x - end.x) + Math.abs(current.y - end.y);
  75. }
  76. // 得到列表中 f 最小的节点
  77. private static Node getMinimumFValueNode(List<Node> nodes) {
  78. int minF = Integer.MAX_VALUE;
  79. Node minNode = null;
  80. for (Node node : nodes) {
  81. if (node.f < minF) {
  82. minF = node.f;
  83. minNode = node;
  84. }
  85. }
  86. return minNode;
  87. }
  88. // 通过节点的parent 构建出路径
  89. private static List<Node> buildPath(Node node) {
  90. List<Node> path = new ArrayList<>();
  91. while (node != null) {
  92. path.add(0, node);
  93. node = node.parent;
  94. }
  95. return path;
  96. }
  97. // 得到邻居节点列表
  98. private static List<Node> getNeighbors(Node node, int[][] grid, int rows, int cols) {
  99. List<Node> neighbors = new ArrayList<>();
  100. int[][] directions = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}}; // 上下左右四个方向
  101. for (int[] direction : directions) {
  102. int newX = node.x + direction[0];
  103. int newY = node.y + direction[1];
  104. if (newX >= 0 && newX < rows && newY >= 0 && newY < cols && grid[newX][newY] == 0) {
  105. // 邻居节点在有效范围内且不是障碍物
  106. Node neighbor = new Node(newX, newY);
  107. neighbors.add(neighbor);
  108. }
  109. }
  110. return neighbors;
  111. }
  112. }

使用优先队列

  1. public class AStarPriority {
  2. public static void main(String[] args) {
  3. int[][] grid = {
  4. {0, 0, 0, 0, 0},
  5. {1, 1, 0, 1, 0},
  6. {0, 0, 0, 0, 0},
  7. {0, 1, 1, 1, 1},
  8. {0, 0, 0, 0, 0}
  9. };
  10. Node start = new Node(0, 0);
  11. Node end = new Node(4, 4);
  12. List<Node> path = findShortestPath(grid, start, end);
  13. if (path != null) {
  14. for (Node node : path) {
  15. System.out.print("(" + node.x + ", " + node.y + ")");
  16. System.out.print("-> ");
  17. }
  18. System.out.print("end");
  19. } else {
  20. System.out.println("No path found");
  21. }
  22. }
  23. public static List<Node> findShortestPath(int[][] grid, Node start, Node end) {
  24. int rows = grid.length;
  25. int cols = grid[0].length;
  26. //优先队列
  27. PriorityQueue<Node> queue = new PriorityQueue<>(rows + cols, Comparator.comparingInt(o -> o.f));
  28. //已访问的节点
  29. Set<Node> visited = new HashSet<>(rows + cols);
  30. //处理开始节点
  31. start.g = 0; //开始几点到当前节点的代价
  32. start.h = guess(start, end); //猜测当前节点到终点的代价
  33. //计算的代价
  34. start.f = start.g + start.h;
  35. //将当前节点添加到优先队列中
  36. queue.offer(start);
  37. while (!queue.isEmpty()) {
  38. //代价最小(f最小)的节点出队列
  39. Node current = queue.poll();
  40. //将当前节点加入 已访问
  41. visited.add(current);
  42. //如果当前节点等于end
  43. if (current.equals(end)) {
  44. //找到了
  45. return buildPath(current);
  46. }
  47. //当前节点的邻居
  48. List<Node> neighbors = currentNeighbors(current, grid, rows, cols);
  49. //遍历邻居
  50. for (Node neighbor : neighbors) {
  51. //访问过 就跳过
  52. if (visited.contains(neighbor))
  53. continue;
  54. //从当前节点到邻居 花费代价1
  55. int tentativeG = current.g + 1;
  56. //如果当前节点没有在队列中 或者 当前路径代价更小
  57. if (!queue.contains(neighbor) || tentativeG < neighbor.g) {
  58. //更新邻居节点
  59. neighbor.parent = current;
  60. neighbor.g = tentativeG;
  61. neighbor.h = guess(neighbor, end);
  62. neighbor.f = neighbor.g + neighbor.h;
  63. if(!queue.contains(neighbor)){
  64. queue.offer(neighbor);
  65. }
  66. }
  67. }
  68. }
  69. return null;
  70. }
  71. private static List<Node> currentNeighbors(Node current, int[][] grid, int rows, int cols) {
  72. //上下左右4个方向
  73. int[][] dir = {{1, 0}, {0, 1}, {-1, 0}, {0, -1}};
  74. List<Node> neighbors = new ArrayList<>();
  75. for (int[] d : dir) {
  76. int nx = current.x + d[0];//下一个几点的x
  77. int ny = current.y + d[1];//下一个几点的y
  78. //是否合法
  79. if (nx < 0 || nx >= rows || ny < 0 || ny >= cols || grid[nx][ny] == 1)
  80. continue;
  81. neighbors.add(new Node(nx, ny));
  82. }
  83. return neighbors;
  84. }
  85. private static List<Node> buildPath(Node current) {
  86. List<Node> path = new ArrayList<>();
  87. Node temp = current;
  88. while (temp != null) {
  89. path.add(0,temp);
  90. temp = temp.parent;
  91. }
  92. return path;
  93. }
  94. private static int guess(Node current, Node end) {
  95. //当前节点到终点的曼哈顿距离: 曼哈顿距离比直线距离好算,不用开方
  96. return Math.abs(current.x - end.x) + Math.abs(current.y - end.y);
  97. }
  98. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/786746
推荐阅读
相关标签
  

闽ICP备14008679号