Skip to content

Feature 2 a(star)search #5890

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
159 changes: 159 additions & 0 deletions src/main/java/com/thealgorithms/backtracking/A(star)-search.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
package com.thealgorithms.backtracking;
// Problem Statement: Find the shortest path from a start node to a goal node using heuristics.

// Example:
// Start (S) -> Goal (G)
// S(0,0) ----- (2,0) ----- G(4,0)

// Heuristic: Manhattan distance.
// Start Node: S(0,0)
// Goal Node: G(4,0)

// A* uses both the actual cost and estimated distance to guide the search.
// Shortest path: S -> (2,0) -> G, with a total cost of 4.


import java.util.*;

// Node class to represent each node in the graph
class Node implements Comparable<Node> {
public int vertex;
public int gCost; // Actual cost to reach this node
public int hCost; // Heuristic cost to goal
public Node parent;

public Node(int vertex, int gCost, int hCost, Node parent) {
this.vertex = vertex;
this.gCost = gCost;
this.hCost = hCost;
this.parent = parent;
}

// f(n) = g(n) + h(n)
public int getFCost() {
return gCost + hCost;
}

@Override
public int compareTo(Node other) {
return Integer.compare(this.getFCost(), other.getFCost());
}
}

// Graph class to represent a weighted graph using adjacency list
class Graph {
private final int V;
private final List<List<Node>> adjacencyList;

public Graph(int V) {
this.V = V;
adjacencyList = new ArrayList<>();
for (int i = 0; i < V; i++) {
adjacencyList.add(new ArrayList<>());
}
}

public void addEdge(int u, int v, int weight) {
adjacencyList.get(u).add(new Node(v, weight, 0, null));
}

public List<List<Node>> getAdjacencyList() {
return adjacencyList;
}

public int getNumberOfVertices() {
return V;
}
}

// Heuristic interface for Strategy Pattern
interface Heuristic {
int calculate(int current, int goal);
}

// Example Heuristic: Manhattan Distance (for grid graphs)
class ManhattanHeuristic implements Heuristic {
private final int[] xCoords;
private final int[] yCoords;

public ManhattanHeuristic(int[] xCoords, int[] yCoords) {
this.xCoords = xCoords;
this.yCoords = yCoords;
}

@Override
public int calculate(int current, int goal) {
return Math.abs(xCoords[current] - xCoords[goal]) + Math.abs(yCoords[current] - yCoords[goal]);
}
}

// Example Heuristic: Euclidean Distance
class EuclideanHeuristic implements Heuristic {
private final int[] xCoords;
private final int[] yCoords;

public EuclideanHeuristic(int[] xCoords, int[] yCoords) {
this.xCoords = xCoords;
this.yCoords = yCoords;
}

@Override
public int calculate(int current, int goal) {
return (int) Math.sqrt(Math.pow(xCoords[current] - xCoords[goal], 2) +
Math.pow(yCoords[current] - yCoords[goal], 2));
}
}

// AStar class for A* algorithm implementation
class AStar {
private final Graph graph;
private final Heuristic heuristic;

public AStar(Graph graph, Heuristic heuristic) {
this.graph = graph;
this.heuristic = heuristic;
}

public List<Integer> findShortestPath(int start, int goal) {
int V = graph.getNumberOfVertices();
PriorityQueue<Node> openSet = new PriorityQueue<>();
Set<Integer> closedSet = new HashSet<>();

Node startNode = new Node(start, 0, heuristic.calculate(start, goal), null);
openSet.add(startNode);

while (!openSet.isEmpty()) {
Node currentNode = openSet.poll();

if (currentNode.vertex == goal) {
return reconstructPath(currentNode);
}

closedSet.add(currentNode.vertex);

for (Node neighbor : graph.getAdjacencyList().get(currentNode.vertex)) {
if (closedSet.contains(neighbor.vertex)) {
continue; // Skip already processed nodes
}

int tentativeGCost = currentNode.gCost + neighbor.gCost;
Node newNeighborNode = new Node(neighbor.vertex, tentativeGCost, heuristic.calculate(neighbor.vertex, goal), currentNode);

openSet.add(newNeighborNode);
}
}

return Collections.emptyList(); // Return an empty list if no path found
}

private List<Integer> reconstructPath(Node goalNode) {
List<Integer> path = new ArrayList<>();
Node currentNode = goalNode;
while (currentNode != null) {
path.add(currentNode.vertex);
currentNode = currentNode.parent;
}
Collections.reverse(path); // Reverse the path to get the correct order
return path;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package com.thealgorithms.backtracking;

import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
import java.util.*;

public class AStarAlgorithmTest {

@Test
public void testBasicGraphWithManhattanHeuristic() {
Graph graph = new Graph(4);
graph.addEdge(0, 1, 1);
graph.addEdge(1, 2, 1);
graph.addEdge(2, 3, 1);
graph.addEdge(0, 3, 10);

int[] xCoords = {0, 1, 2, 3};
int[] yCoords = {0, 0, 0, 0};

Heuristic manhattanHeuristic = new ManhattanHeuristic(xCoords, yCoords);
AStar aStar = new AStar(graph, manhattanHeuristic);

List<Integer> path = aStar.findShortestPath(0, 3);

List<Integer> expectedPath = Arrays.asList(0, 1, 2, 3);
assertEquals(expectedPath, path);
}

@Test
public void testBasicGraphWithEuclideanHeuristic() {
Graph graph = new Graph(4);
graph.addEdge(0, 1, 2);
graph.addEdge(1, 2, 2);
graph.addEdge(2, 3, 2);
graph.addEdge(0, 3, 5);

int[] xCoords = {0, 2, 4, 6};
int[] yCoords = {0, 0, 0, 0};

Heuristic euclideanHeuristic = new EuclideanHeuristic(xCoords, yCoords);
AStar aStar = new AStar(graph, euclideanHeuristic);

List<Integer> path = aStar.findShortestPath(0, 3);

List<Integer> expectedPath = Arrays.asList(0, 3);
assertEquals(expectedPath, path);
}

@Test
public void testDisconnectedGraph() {
Graph graph = new Graph(4);
graph.addEdge(0, 1, 2);
graph.addEdge(1, 2, 2);

int[] xCoords = {0, 2, 4, 6};
int[] yCoords = {0, 0, 0, 0};

Heuristic manhattanHeuristic = new ManhattanHeuristic(xCoords, yCoords);
AStar aStar = new AStar(graph, manhattanHeuristic);

List<Integer> path = aStar.findShortestPath(0, 3);

assertTrue(path.isEmpty());
}
}
Loading