From 594c9d14e9c17dfdb7a990d6a3c3443495a2dfa2 Mon Sep 17 00:00:00 2001 From: Hardik Pawar Date: Sun, 6 Oct 2024 12:18:36 +0530 Subject: [PATCH 1/5] Add tests for `AStar.java`, enhance documentation --- .../datastructures/graphs/AStar.java | 139 ++++++------------ .../datastructures/graphs/AStarTest.java | 52 +++++++ 2 files changed, 100 insertions(+), 91 deletions(-) create mode 100644 src/test/java/com/thealgorithms/datastructures/graphs/AStarTest.java diff --git a/src/main/java/com/thealgorithms/datastructures/graphs/AStar.java b/src/main/java/com/thealgorithms/datastructures/graphs/AStar.java index 54fb5fba5c1b..d176eddfed48 100644 --- a/src/main/java/com/thealgorithms/datastructures/graphs/AStar.java +++ b/src/main/java/com/thealgorithms/datastructures/graphs/AStar.java @@ -1,6 +1,3 @@ -/* - Time Complexity = O(E), where E is equal to the number of edges - */ package com.thealgorithms.datastructures.graphs; import java.util.ArrayList; @@ -9,17 +6,22 @@ import java.util.List; import java.util.PriorityQueue; +/** + * AStar class implements the A* pathfinding algorithm to find the shortest path in a graph. + * The graph is represented using an adjacency list, and the algorithm uses a heuristic to estimate + * the cost to reach the destination node. + * Time Complexity = O(E), where E is equal to the number of edges + */ public final class AStar { private AStar() { } - private static class Graph { - - // Graph's structure can be changed only applying changes to this class. - + /** + * Represents a graph using an adjacency list. + */ + static class Graph { private ArrayList> graph; - // Initialise ArrayLists in Constructor Graph(int size) { this.graph = new ArrayList<>(); for (int i = 0; i < size; i++) { @@ -31,15 +33,17 @@ private ArrayList getNeighbours(int from) { return this.graph.get(from); } - // Graph is bidirectional, for just one direction remove second instruction of this method. + // Add a bidirectional edge to the graph private void addEdge(Edge edge) { this.graph.get(edge.getFrom()).add(new Edge(edge.getFrom(), edge.getTo(), edge.getWeight())); this.graph.get(edge.getTo()).add(new Edge(edge.getTo(), edge.getFrom(), edge.getWeight())); } } + /** + * Represents an edge in the graph with a start node, end node, and weight. + */ private static class Edge { - private int from; private int to; private int weight; @@ -63,12 +67,13 @@ public int getWeight() { } } - // class to iterate during the algorithm execution, and also used to return the solution. - private static class PathAndDistance { - - private int distance; // distance advanced so far. - private ArrayList path; // list of visited nodes in this path. - private int estimated; // heuristic value associated to the last node od the path (current node). + /** + * Contains information about the path and its total distance. + */ + static class PathAndDistance { + private int distance; // total distance from the start node + private ArrayList path; // list of nodes in the path + private int estimated; // heuristic estimate for reaching the destination PathAndDistance(int distance, ArrayList path, int estimated) { this.distance = distance; @@ -97,102 +102,54 @@ private void printSolution() { } } - private static void initializeGraph(Graph graph, ArrayList data) { + // Initializes the graph with edges defined in the input data + static void initializeGraph(Graph graph, ArrayList data) { for (int i = 0; i < data.size(); i += 4) { graph.addEdge(new Edge(data.get(i), data.get(i + 1), data.get(i + 2))); } - /* - .x. node - (y) cost - - or | or / bidirectional connection - - ( 98)- .7. -(86)- .4. - | - ( 85)- .17. -(142)- .18. -(92)- .8. -(87)- .11. - | - . 1. -------------------- (160) - | \ | - (211) \ .6. - | \ | - . 5. (101)-.13. -(138) (115) - | | | / - ( 99) ( 97) | / - | | | / - .12. -(151)- .15. -(80)- .14. | / - | | | | / - ( 71) (140) (146)- .2. -(120) - | | | - .19. -( 75)- . 0. .10. -(75)- .3. - | | - (118) ( 70) - | | - .16. -(111)- .9. - */ - } - - public static void main(String[] args) { - // heuristic function optimistic values - int[] heuristic = { - 366, - 0, - 160, - 242, - 161, - 178, - 77, - 151, - 226, - 244, - 241, - 234, - 380, - 98, - 193, - 253, - 329, - 80, - 199, - 374, - }; - - Graph graph = new Graph(20); - ArrayList graphData = new ArrayList<>(Arrays.asList(0, 19, 75, null, 0, 15, 140, null, 0, 16, 118, null, 19, 12, 71, null, 12, 15, 151, null, 16, 9, 111, null, 9, 10, 70, null, 10, 3, 75, null, 3, 2, 120, null, 2, 14, 146, null, 2, 13, 138, null, 2, 6, 115, null, 15, 14, 80, null, - 15, 5, 99, null, 14, 13, 97, null, 5, 1, 211, null, 13, 1, 101, null, 6, 1, 160, null, 1, 17, 85, null, 17, 7, 98, null, 7, 4, 86, null, 17, 18, 142, null, 18, 8, 92, null, 8, 11, 87)); - initializeGraph(graph, graphData); - - PathAndDistance solution = aStar(3, 1, graph, heuristic); - solution.printSolution(); } + /** + * Implements the A* pathfinding algorithm to find the shortest path from a start node to a destination node. + * + * @param from the starting node + * @param to the destination node + * @param graph the graph representation of the problem + * @param heuristic the heuristic estimates for each node + * @return a PathAndDistance object containing the shortest path and its distance + */ public static PathAndDistance aStar(int from, int to, Graph graph, int[] heuristic) { - // nodes are prioritised by the less value of the current distance of their paths, and the - // estimated value - // given by the heuristic function to reach the destination point from the current point. - PriorityQueue queue = new PriorityQueue<>(Comparator.comparingInt(a -> (a.getDistance() + a.getEstimated()))); + // PriorityQueue to explore nodes based on their distance and estimated cost to reach the destination + PriorityQueue queue = new PriorityQueue<>( + Comparator.comparingInt(a -> (a.getDistance() + a.getEstimated())) + ); - // dummy data to start the algorithm from the beginning point - queue.add(new PathAndDistance(0, new ArrayList<>(List.of(from)), 0)); + // Start with the initial node + queue.add(new PathAndDistance(0, new ArrayList<>(List.of(from)), heuristic[from])); boolean solutionFound = false; PathAndDistance currentData = new PathAndDistance(-1, null, -1); + while (!queue.isEmpty() && !solutionFound) { - currentData = queue.poll(); // first in the queue, best node so keep exploring. - int currentPosition = currentData.getPath().get(currentData.getPath().size() - 1); // current node. + currentData = queue.poll(); // get the best node from the queue + int currentPosition = currentData.getPath().get(currentData.getPath().size() - 1); // current node + + // Check if the destination has been reached if (currentPosition == to) { solutionFound = true; } else { for (Edge edge : graph.getNeighbours(currentPosition)) { - if (!currentData.getPath().contains(edge.getTo())) { // Avoid Cycles + // Avoid cycles by checking if the next node is already in the path + if (!currentData.getPath().contains(edge.getTo())) { ArrayList updatedPath = new ArrayList<>(currentData.getPath()); - updatedPath.add(edge.getTo()); // Add the new node to the path, update the distance, - // and the heuristic function value associated to that path. + updatedPath.add(edge.getTo()); + + // Update the distance and heuristic for the new path queue.add(new PathAndDistance(currentData.getDistance() + edge.getWeight(), updatedPath, heuristic[edge.getTo()])); } } } } return (solutionFound) ? currentData : new PathAndDistance(-1, null, -1); - // Out of while loop, if there is a solution, the current Data stores the optimal path, and - // its distance } } diff --git a/src/test/java/com/thealgorithms/datastructures/graphs/AStarTest.java b/src/test/java/com/thealgorithms/datastructures/graphs/AStarTest.java new file mode 100644 index 000000000000..ef7fea1cbe9d --- /dev/null +++ b/src/test/java/com/thealgorithms/datastructures/graphs/AStarTest.java @@ -0,0 +1,52 @@ +package com.thealgorithms.datastructures.graphs; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +import java.util.ArrayList; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import java.util.Arrays; + +public class AStarTest { + + private AStar.Graph graph; + private int[] heuristic; + + @BeforeEach + public void setUp() { + // Initialize graph and heuristic values for testing + graph = new AStar.Graph(5); + ArrayList graphData = new ArrayList<>(Arrays.asList( + 0, 1, 1, null, + 0, 2, 2, null, + 1, 3, 1, null, + 2, 3, 1, null, + 3, 4, 1, null + )); + AStar.initializeGraph(graph, graphData); + + heuristic = new int[] {5, 4, 3, 2, 0}; // Heuristic values for each node + } + + @Test + public void testAStarFindsPath() { + AStar.PathAndDistance result = AStar.aStar(0, 4, graph, heuristic); + assertEquals(3, result.getDistance(), "Expected distance from 0 to 4 is 3"); + assertEquals(Arrays.asList(0, 1, 3, 4), result.getPath(), "Expected path from 0 to 4"); + } + + @Test + public void testAStarPathNotFound() { + AStar.PathAndDistance result = AStar.aStar(0, 5, graph, heuristic); // Node 5 does not exist + assertEquals(-1, result.getDistance(), "Expected distance when path not found is -1"); + assertNull(result.getPath(), "Expected path should be null when no path exists"); + } + + @Test + public void testAStarSameNode() { + AStar.PathAndDistance result = AStar.aStar(0, 0, graph, heuristic); + assertEquals(0, result.getDistance(), "Expected distance from 0 to 0 is 0"); + assertEquals(Arrays.asList(0), result.getPath(), "Expected path should only contain the start node"); + } +} From 0215a8106a38fd78a235dd84d24be3c5170a71d4 Mon Sep 17 00:00:00 2001 From: Hardvan Date: Sun, 6 Oct 2024 06:48:53 +0000 Subject: [PATCH 2/5] Update directory --- DIRECTORY.md | 1 + 1 file changed, 1 insertion(+) diff --git a/DIRECTORY.md b/DIRECTORY.md index 1bad5d3b98a3..94fa4ae64c8e 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -671,6 +671,7 @@ * dynamicarray * [DynamicArrayTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/dynamicarray/DynamicArrayTest.java) * graphs + * [AStarTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/graphs/AStarTest.java) * [BoruvkaAlgorithmTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/graphs/BoruvkaAlgorithmTest.java) * [DijkstraAlgorithmTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/graphs/DijkstraAlgorithmTest.java) * [EdmondsBlossomAlgorithmTest](https://github.com/TheAlgorithms/Java/blob/master/src/test/java/com/thealgorithms/datastructures/graphs/EdmondsBlossomAlgorithmTest.java) From e99b06cfa8d761b17588137b5a42a8eee7175abf Mon Sep 17 00:00:00 2001 From: Hardik Pawar Date: Sun, 6 Oct 2024 12:40:37 +0530 Subject: [PATCH 3/5] Fix --- .../com/thealgorithms/datastructures/graphs/AStar.java | 4 +--- .../thealgorithms/datastructures/graphs/AStarTest.java | 10 ++-------- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/src/main/java/com/thealgorithms/datastructures/graphs/AStar.java b/src/main/java/com/thealgorithms/datastructures/graphs/AStar.java index d176eddfed48..bdca772f4d7d 100644 --- a/src/main/java/com/thealgorithms/datastructures/graphs/AStar.java +++ b/src/main/java/com/thealgorithms/datastructures/graphs/AStar.java @@ -120,9 +120,7 @@ static void initializeGraph(Graph graph, ArrayList data) { */ public static PathAndDistance aStar(int from, int to, Graph graph, int[] heuristic) { // PriorityQueue to explore nodes based on their distance and estimated cost to reach the destination - PriorityQueue queue = new PriorityQueue<>( - Comparator.comparingInt(a -> (a.getDistance() + a.getEstimated())) - ); + PriorityQueue queue = new PriorityQueue<>(Comparator.comparingInt(a -> (a.getDistance() + a.getEstimated()))); // Start with the initial node queue.add(new PathAndDistance(0, new ArrayList<>(List.of(from)), heuristic[from])); diff --git a/src/test/java/com/thealgorithms/datastructures/graphs/AStarTest.java b/src/test/java/com/thealgorithms/datastructures/graphs/AStarTest.java index ef7fea1cbe9d..dce5a6ed4b69 100644 --- a/src/test/java/com/thealgorithms/datastructures/graphs/AStarTest.java +++ b/src/test/java/com/thealgorithms/datastructures/graphs/AStarTest.java @@ -4,9 +4,9 @@ import static org.junit.jupiter.api.Assertions.assertNull; import java.util.ArrayList; +import java.util.Arrays; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import java.util.Arrays; public class AStarTest { @@ -17,13 +17,7 @@ public class AStarTest { public void setUp() { // Initialize graph and heuristic values for testing graph = new AStar.Graph(5); - ArrayList graphData = new ArrayList<>(Arrays.asList( - 0, 1, 1, null, - 0, 2, 2, null, - 1, 3, 1, null, - 2, 3, 1, null, - 3, 4, 1, null - )); + ArrayList graphData = new ArrayList<>(Arrays.asList(0, 1, 1, null, 0, 2, 2, null, 1, 3, 1, null, 2, 3, 1, null, 3, 4, 1, null)); AStar.initializeGraph(graph, graphData); heuristic = new int[] {5, 4, 3, 2, 0}; // Heuristic values for each node From 3ae67cdfa27f281aa0c12615c51d45639f24b174 Mon Sep 17 00:00:00 2001 From: Hardik Pawar Date: Sun, 6 Oct 2024 12:41:02 +0530 Subject: [PATCH 4/5] Fix --- src/main/java/com/thealgorithms/datastructures/graphs/AStar.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/com/thealgorithms/datastructures/graphs/AStar.java b/src/main/java/com/thealgorithms/datastructures/graphs/AStar.java index bdca772f4d7d..dffb4b3ae6ac 100644 --- a/src/main/java/com/thealgorithms/datastructures/graphs/AStar.java +++ b/src/main/java/com/thealgorithms/datastructures/graphs/AStar.java @@ -1,7 +1,6 @@ package com.thealgorithms.datastructures.graphs; import java.util.ArrayList; -import java.util.Arrays; import java.util.Comparator; import java.util.List; import java.util.PriorityQueue; From f141ba868e5ad5da4d286da51bff8e60d1991ab9 Mon Sep 17 00:00:00 2001 From: Hardik Pawar Date: Sun, 6 Oct 2024 12:43:41 +0530 Subject: [PATCH 5/5] Remove print method --- .../com/thealgorithms/datastructures/graphs/AStar.java | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/main/java/com/thealgorithms/datastructures/graphs/AStar.java b/src/main/java/com/thealgorithms/datastructures/graphs/AStar.java index dffb4b3ae6ac..460c05e04403 100644 --- a/src/main/java/com/thealgorithms/datastructures/graphs/AStar.java +++ b/src/main/java/com/thealgorithms/datastructures/graphs/AStar.java @@ -91,14 +91,6 @@ public ArrayList getPath() { public int getEstimated() { return estimated; } - - private void printSolution() { - if (this.path != null) { - System.out.println("Optimal path: " + this.path + ", distance: " + this.distance); - } else { - System.out.println("There is no path available to connect the points"); - } - } } // Initializes the graph with edges defined in the input data