|
| 1 | +package com.thealgorithms.backtracking; |
| 2 | +// Problem Statement: Find the shortest path from a start node to a goal node using heuristics. |
| 3 | + |
| 4 | +// Example: |
| 5 | +// Start (S) -> Goal (G) |
| 6 | +// S(0,0) ----- (2,0) ----- G(4,0) |
| 7 | + |
| 8 | +// Heuristic: Manhattan distance. |
| 9 | +// Start Node: S(0,0) |
| 10 | +// Goal Node: G(4,0) |
| 11 | + |
| 12 | +// A* uses both the actual cost and estimated distance to guide the search. |
| 13 | +// Shortest path: S -> (2,0) -> G, with a total cost of 4. |
| 14 | + |
| 15 | + |
| 16 | +import java.util.*; |
| 17 | + |
| 18 | +// Node class to represent each node in the graph |
| 19 | +class Node implements Comparable<Node> { |
| 20 | + public int vertex; |
| 21 | + public int gCost; // Actual cost to reach this node |
| 22 | + public int hCost; // Heuristic cost to goal |
| 23 | + public Node parent; |
| 24 | + |
| 25 | + public Node(int vertex, int gCost, int hCost, Node parent) { |
| 26 | + this.vertex = vertex; |
| 27 | + this.gCost = gCost; |
| 28 | + this.hCost = hCost; |
| 29 | + this.parent = parent; |
| 30 | + } |
| 31 | + |
| 32 | + // f(n) = g(n) + h(n) |
| 33 | + public int getFCost() { |
| 34 | + return gCost + hCost; |
| 35 | + } |
| 36 | + |
| 37 | + @Override |
| 38 | + public int compareTo(Node other) { |
| 39 | + return Integer.compare(this.getFCost(), other.getFCost()); |
| 40 | + } |
| 41 | +} |
| 42 | + |
| 43 | +// Graph class to represent a weighted graph using adjacency list |
| 44 | +class Graph { |
| 45 | + private final int V; |
| 46 | + private final List<List<Node>> adjacencyList; |
| 47 | + |
| 48 | + public Graph(int V) { |
| 49 | + this.V = V; |
| 50 | + adjacencyList = new ArrayList<>(); |
| 51 | + for (int i = 0; i < V; i++) { |
| 52 | + adjacencyList.add(new ArrayList<>()); |
| 53 | + } |
| 54 | + } |
| 55 | + |
| 56 | + public void addEdge(int u, int v, int weight) { |
| 57 | + adjacencyList.get(u).add(new Node(v, weight, 0, null)); |
| 58 | + } |
| 59 | + |
| 60 | + public List<List<Node>> getAdjacencyList() { |
| 61 | + return adjacencyList; |
| 62 | + } |
| 63 | + |
| 64 | + public int getNumberOfVertices() { |
| 65 | + return V; |
| 66 | + } |
| 67 | +} |
| 68 | + |
| 69 | +// Heuristic interface for Strategy Pattern |
| 70 | +interface Heuristic { |
| 71 | + int calculate(int current, int goal); |
| 72 | +} |
| 73 | + |
| 74 | +// Example Heuristic: Manhattan Distance (for grid graphs) |
| 75 | +class ManhattanHeuristic implements Heuristic { |
| 76 | + private final int[] xCoords; |
| 77 | + private final int[] yCoords; |
| 78 | + |
| 79 | + public ManhattanHeuristic(int[] xCoords, int[] yCoords) { |
| 80 | + this.xCoords = xCoords; |
| 81 | + this.yCoords = yCoords; |
| 82 | + } |
| 83 | + |
| 84 | + @Override |
| 85 | + public int calculate(int current, int goal) { |
| 86 | + return Math.abs(xCoords[current] - xCoords[goal]) + Math.abs(yCoords[current] - yCoords[goal]); |
| 87 | + } |
| 88 | +} |
| 89 | + |
| 90 | +// Example Heuristic: Euclidean Distance |
| 91 | +class EuclideanHeuristic implements Heuristic { |
| 92 | + private final int[] xCoords; |
| 93 | + private final int[] yCoords; |
| 94 | + |
| 95 | + public EuclideanHeuristic(int[] xCoords, int[] yCoords) { |
| 96 | + this.xCoords = xCoords; |
| 97 | + this.yCoords = yCoords; |
| 98 | + } |
| 99 | + |
| 100 | + @Override |
| 101 | + public int calculate(int current, int goal) { |
| 102 | + return (int) Math.sqrt(Math.pow(xCoords[current] - xCoords[goal], 2) + |
| 103 | + Math.pow(yCoords[current] - yCoords[goal], 2)); |
| 104 | + } |
| 105 | +} |
| 106 | + |
| 107 | +// AStar class for A* algorithm implementation |
| 108 | +class AStar { |
| 109 | + private final Graph graph; |
| 110 | + private final Heuristic heuristic; |
| 111 | + |
| 112 | + public AStar(Graph graph, Heuristic heuristic) { |
| 113 | + this.graph = graph; |
| 114 | + this.heuristic = heuristic; |
| 115 | + } |
| 116 | + |
| 117 | + public List<Integer> findShortestPath(int start, int goal) { |
| 118 | + int V = graph.getNumberOfVertices(); |
| 119 | + PriorityQueue<Node> openSet = new PriorityQueue<>(); |
| 120 | + Set<Integer> closedSet = new HashSet<>(); |
| 121 | + |
| 122 | + Node startNode = new Node(start, 0, heuristic.calculate(start, goal), null); |
| 123 | + openSet.add(startNode); |
| 124 | + |
| 125 | + while (!openSet.isEmpty()) { |
| 126 | + Node currentNode = openSet.poll(); |
| 127 | + |
| 128 | + if (currentNode.vertex == goal) { |
| 129 | + return reconstructPath(currentNode); |
| 130 | + } |
| 131 | + |
| 132 | + closedSet.add(currentNode.vertex); |
| 133 | + |
| 134 | + for (Node neighbor : graph.getAdjacencyList().get(currentNode.vertex)) { |
| 135 | + if (closedSet.contains(neighbor.vertex)) { |
| 136 | + continue; // Skip already processed nodes |
| 137 | + } |
| 138 | + |
| 139 | + int tentativeGCost = currentNode.gCost + neighbor.gCost; |
| 140 | + Node newNeighborNode = new Node(neighbor.vertex, tentativeGCost, heuristic.calculate(neighbor.vertex, goal), currentNode); |
| 141 | + |
| 142 | + openSet.add(newNeighborNode); |
| 143 | + } |
| 144 | + } |
| 145 | + |
| 146 | + return Collections.emptyList(); // Return an empty list if no path found |
| 147 | + } |
| 148 | + |
| 149 | + private List<Integer> reconstructPath(Node goalNode) { |
| 150 | + List<Integer> path = new ArrayList<>(); |
| 151 | + Node currentNode = goalNode; |
| 152 | + while (currentNode != null) { |
| 153 | + path.add(currentNode.vertex); |
| 154 | + currentNode = currentNode.parent; |
| 155 | + } |
| 156 | + Collections.reverse(path); // Reverse the path to get the correct order |
| 157 | + return path; |
| 158 | + } |
| 159 | +} |
0 commit comments