A* Algorithm Implementation using Java & Python

Share with friends
Save Story for Later (0)
Please login to bookmark Close

Introduction to A* Algorithm

The A* (A-star) algorithm is highly popular. It is widely used in pathfinding algorithms in computer science. It is also common in artificial intelligence. It finds the shortest path between two nodes in a weighted graph. This makes it particularly useful in applications like game development, robotics, and network routing.

Why Learn the A* Algorithm?

  • Efficiency: A* is known for its optimal efficiency in finding the shortest path.
  • Versatility: It can be used in various applications from gaming to GPS navigation.
  • Foundation: Understanding A* helps build a strong foundation for learning other advanced algorithms.

How Does the A* Algorithm Work?

The A* algorithm combines the strengths of Dijkstra’s algorithm and Greedy Best-First-Search. It uses a heuristic to guide its search, which helps it find the shortest path more efficiently.

Key Concepts:

  1. Nodes and Edges: The algorithm works on graphs composed of nodes (points) and edges (connections between points).
  2. Cost Function (f): The algorithm uses a cost function, f(n) = g(n) + h(n), where:
    • g(n) is the cost from the start node to the current node n.
    • h(n) is the heuristic estimate of the cost from n to the goal.
  3. Open and Closed Sets:
    • Open Set: The set of nodes to be evaluated.
    • Closed Set: The set of nodes already evaluated.

Step-by-Step Process:

  1. Initialize the Open Set: Start with the initial node.
  2. Current Node Selection: Select the node with the lowest f value from the open set.
  3. Goal Check: If the current node is the goal, reconstruct the path and end.
  4. Neighbor Evaluation: For each neighbor of the current node:
    • Calculate the tentative g score.
    • If this score is better than the previous score, update the path.
    • Update the neighbor’s f value and add it to the open set if not already present.
  5. Repeat: Continue until the open set is empty or the goal is reached.

Example Diagram:

   Start
|
[A]--1--[B]--2--[C]
| / | / |
3| 1 5 2 |1
| / | / |
[D]--2---[E]--1--[F]--Goal
  • Start to Goal via the shortest path [A, B, E, F].

Visualizing A* Algorithm

Mind Map

Here’s a visual representation of the A* algorithm:

                A* Algorithm
|
+-----------+------------+
| |
Nodes Edges
| |
+-----+-----+ +-----+-----+
| | | |
Open Set Closed Set Cost Function Heuristic

Flowchart

Implementation of A* Algorithm

Let’s look at the implementation of the A* algorithm in both Java and Python.

Java Implementation

import java.util.*;

class Node {
    public String name;
    public List<Edge> neighbors;
    public int g, h, f;
    public Node parent;

    public Node(String name, int h) {
        this.name = name;
        this.h = h;
        this.g = 0;
        this.f = 0;
        this.neighbors = new ArrayList<>();
    }

    public void addNeighbor(Node neighbor, int cost) {
        neighbors.add(new Edge(neighbor, cost));
    }
}

class Edge {
    public Node node;
    public int cost;

    public Edge(Node node, int cost) {
        this.node = node;
        this.cost = cost;
    }
}

public class AStarAlgorithm {
    public static List<Node> aStar(Node start, Node goal) {
        PriorityQueue<Node> openSet = new PriorityQueue<>(Comparator.comparingInt(node -> node.f));
        Set<Node> closedSet = new HashSet<>();

        openSet.add(start);

        while (!openSet.isEmpty()) {
            Node current = openSet.poll();
            if (current == goal) {
                return reconstructPath(current);
            }

            closedSet.add(current);

            for (Edge edge : current.neighbors) {
                Node neighbor = edge.node;
                if (closedSet.contains(neighbor)) continue;

                int tentativeG = current.g + edge.cost;
                if (!openSet.contains(neighbor) || tentativeG < neighbor.g) {
                    neighbor.parent = current;
                    neighbor.g = tentativeG;
                    neighbor.f = neighbor.g + neighbor.h;

                    if (!openSet.contains(neighbor)) {
                        openSet.add(neighbor);
                    }
                }
            }
        }

        return Collections.emptyList(); // No path found
    }

    private static List<Node> reconstructPath(Node current) {
        List<Node> path = new ArrayList<>();
        while (current != null) {
            path.add(current);
            current = current.parent;
        }
        Collections.reverse(path);
        return path;
    }

    public static void main(String[] args) {
        Node start = new Node("Start", 4);
        Node a = new Node("A", 2);
        Node b = new Node("B", 2);
        Node c = new Node("C", 1);
        Node goal = new Node("Goal", 0);

        start.addNeighbor(a, 1);
        a.addNeighbor(b, 1);
        b.addNeighbor(c, 1);
        c.addNeighbor(goal, 1);

        List<Node> path = aStar(start, goal);
        for (Node node : path) {
            System.out.print(node.name + " ");
        }
    }
}

Python Implementation

import heapq

class Node:
    def __init__(self, name, h):
        self.name = name
        self.g = 0
        self.h = h
        self.f = 0
        self.neighbors = []
        self.parent = None

    def add_neighbor(self, neighbor, cost):
        self.neighbors.append((neighbor, cost))

    def __lt__(self, other):
        return self.f < other.f

def a_star(start, goal):
    open_set = []
    heapq.heappush(open_set, start)
    closed_set = set()

    while open_set:
        current = heapq.heappop(open_set)
        if current == goal:
            return reconstruct_path(current)

        closed_set.add(current)

        for neighbor, cost in current.neighbors:
            if neighbor in closed_set:
                continue

            tentative_g = current.g + cost
            if tentative_g < neighbor.g or neighbor not in open_set:
                neighbor.parent = current
                neighbor.g = tentative_g
                neighbor.f = neighbor.g + neighbor.h

                if neighbor not in open_set:
                    heapq.heappush(open_set, neighbor)

    return []  # No path found

def reconstruct_path(current):
    path = []
    while current:
        path.append(current.name)
        current = current.parent
    return path[::-1]

# Example Usage
start = Node("Start", 4)
a = Node("A", 2)
b = Node("B", 2)
c = Node("C", 1)
goal = Node("Goal", 0)

start.add_neighbor(a, 1)
a.add_neighbor(b, 1)
b.add_neighbor(c, 1)
c.add_neighbor(goal, 1)

path = a_star(start, goal)
print(" -> ".join(path))

Conclusion

The A* algorithm is a powerful and efficient pathfinding algorithm used in various applications. By understanding the key concepts and step-by-step process, even beginners can implement and utilize this algorithm. With the provided implementations in Java and Python, you can now experiment with and apply the A* algorithm to your own projects. Happy coding!

Article Contributors

  • Alfred Algo
    (Author)
    Chief Algorithms Scientist, QABash

    Alfred Algo is a renowned expert in data structures and algorithms, celebrated for his ability to simplify complex concepts. With a background in computer science from a prestigious university, Alfred has spent over a decade teaching and mentoring aspiring programmers. He is the author at the popular blog "The Testing Times," where he shares tips, tutorials, and insights into mastering DSA.

  • Ishan Dev Shukl
    (Reviewer)
    SDET Manager, Nykaa

    With 13+ years in SDET leadership, I drive quality and innovation through Test Strategies and Automation. I lead Testing Center of Excellence, ensuring high-quality products across Frontend, Backend, and App Testing. "Quality is in the details" defines my approach—creating seamless, impactful user experiences. I embrace challenges, learn from failure, and take risks to drive success.

Subscribe to QABash Weekly 💥

Dominate – Stay Ahead of 99% Testers!

Leave a Reply

Scroll to Top