-
Notifications
You must be signed in to change notification settings - Fork 21.1k
Expand file tree
/
Copy pathAStarSearch.java
More file actions
152 lines (128 loc) · 3.93 KB
/
AStarSearch.java
File metadata and controls
152 lines (128 loc) · 3.93 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
package com.thealgorithms.others;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.PriorityQueue;
/**
* A* (A-Star) Search Algorithm implementation for finding the shortest path
* between two nodes in a graph.
*
* <p>This algorithm uses a heuristic to guide its search, making it more efficient
* than Dijkstra’s algorithm in many cases.
*
* <p>f(n) = g(n) + h(n)
* where:
* - g(n): cost from start node to current node
* - h(n): estimated cost from current node to goal (heuristic)
*
* <p>Time Complexity:
* - Worst case: O(E log V)
*
* <p>Space Complexity:
* - O(V)
*
* <p>Use Cases:
* - Pathfinding (maps, games)
* - AI planning
*
* @author Suraj Devatha
*/
public final class AStarSearch {
private AStarSearch() {
}
/**
* Finds shortest path using A*.
*
* @param graph adjacency list
* @param start start node
* @param goal goal node
* @param heuristic heuristic function
* @return list of nodes representing shortest path
*/
public static List<Node> findPath(Map<Node, List<Edge>> graph, Node start, Node goal, Heuristic heuristic) {
Map<Node, Double> gScore = new HashMap<>();
Map<Node, Double> fScore = new HashMap<>();
Map<Node, Node> cameFrom = new HashMap<>();
PriorityQueue<Node> openSet = new PriorityQueue<>(Comparator.comparingDouble(fScore::get));
gScore.put(start, 0.0);
fScore.put(start, heuristic.estimate(start, goal));
openSet.add(start);
while (!openSet.isEmpty()) {
Node current = openSet.poll();
if (current.equals(goal)) {
return reconstructPath(cameFrom, current);
}
for (Edge edge : graph.getOrDefault(current, Collections.emptyList())) {
Node neighbor = edge.target;
double tentativeG = gScore.get(current) + edge.cost;
if (tentativeG < gScore.getOrDefault(neighbor, Double.POSITIVE_INFINITY)) {
cameFrom.put(neighbor, current);
gScore.put(neighbor, tentativeG);
fScore.put(neighbor, tentativeG + heuristic.estimate(neighbor, goal));
if (!openSet.contains(neighbor)) {
openSet.add(neighbor);
}
}
}
}
return Collections.emptyList(); // No path found
}
/**
* Reconstructs path from goal to start.
*/
private static List<Node> reconstructPath(Map<Node, Node> cameFrom, Node current) {
List<Node> path = new ArrayList<>();
path.add(current);
while (cameFrom.containsKey(current)) {
current = cameFrom.get(current);
path.add(current);
}
Collections.reverse(path);
return path;
}
/**
* Heuristic interface (can plug different heuristics).
*/
public interface Heuristic {
double estimate(Node current, Node goal);
}
/**
* Node class representing a vertex in the graph.
*/
static class Node {
String id;
Node(String id) {
this.id = id;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof Node)) {
return false;
}
Node node = (Node) o;
return Objects.equals(id, node.id);
}
@Override
public int hashCode() {
return Objects.hash(id);
}
}
/**
* Edge class representing weighted connections.
*/
static class Edge {
Node target;
double cost;
Edge(Node target, double cost) {
this.target = target;
this.cost = cost;
}
}
}