A* Search Algorithm

The smartest way to find the shortest path — explained so simply you'll never forget it

The Simplest Explanation

Imagine you're lost in a city and want to reach your hotel. You have two pieces of information:

  1. How far you've already walked (you know this exactly)
  2. How far the hotel looks from where you are (you can see the skyline and estimate)

A* says: always pick the next move that minimizes (distance walked so far) + (estimated distance remaining).

That's it. If you remember nothing else, remember this: pick the node with the smallest "cost so far + guess to goal".

f(n)=g(n)+h(n)f(n) = g(n) + h(n)

  • g(n)g(n) = exact cost from start to nn (this is known, no guessing)
  • h(n)h(n) = estimated cost from nn to goal (this is the guess/heuristic)
  • f(n)f(n) = total estimated cost of the best path through nn
The One Rule For The Guess

Your guess h(n)h(n) must never overestimate the real remaining distance. If the hotel is 5 km away, you can guess 3 km (underestimate = fine) or 5 km (exact = fine), but never 7 km (overestimate = breaks optimality). This is called admissibility.

Why A* Works (Intuition)

Think of it this way:

  • If you only use g(n)g(n) (ignore the guess), you get Dijkstra's algorithm — it explores in every direction equally, like flood fill. It works but is slow because it wastes time going the wrong way.
  • If you only use h(n)h(n) (ignore the cost so far), you get Greedy Best-First Search — it beelines toward the goal but might find a suboptimal path because it doesn't account for how expensive its route has been.
  • A combines both* — it heads toward the goal (thanks to hh) but doesn't take unnecessarily expensive routes (thanks to gg).

The Algorithm (Step by Step)

  1. Put the start node in the open list with g=0g = 0, f=0+h(start)f = 0 + h(\text{start})
  2. While the open list is not empty:
    • Pick the node with the lowest f(n)f(n) from the open list — call it current
    • If current is the goal → done! Reconstruct path
    • Move current to the closed list
    • For each neighbor of current:
      • Calculate g(neighbor)=g(current)+edge costg(\text{neighbor}) = g(\text{current}) + \text{edge cost}
      • Calculate f(neighbor)=g(neighbor)+h(neighbor)f(\text{neighbor}) = g(\text{neighbor}) + h(\text{neighbor})
      • If neighbor is in closed list with a lower gg → skip
      • If neighbor is in open list with a lower gg → skip
      • Otherwise → add (or update) neighbor in the open list
  3. If open list is empty and goal not found → no path exists
The Tie-Breaking Rule

When multiple nodes have the same ff-value, you can break ties by:

  • Preferring the node with higher gg (i.e., closer to the goal — this prefers depth-first exploration and is the standard approach)
  • Preferring the node with lower gg (closer to start — shallower exploration)
  • Alphabetical order (for exam questions where you need a deterministic answer — always check the question's convention)

Admissibility and Consistency

Admissibility

A heuristic hh is admissible if it never overestimates the true cost to the goal:

0h(n)h(n)for all n0 \leq h(n) \leq h^*(n) \quad \text{for all } n

where h(n)h^*(n) is the actual optimal cost from nn to the goal.

Why it matters: If hh is admissible, A* is guaranteed to find the optimal solution. If hh overestimates even once, A* might skip the optimal path.

Consistency (Monotonicity)

A heuristic is consistent if for every node nn and its successor nn' reached by action aa:

h(n)c(n,a,n)+h(n)h(n) \leq c(n, a, n') + h(n')

This means: the heuristic drop from nn to nn' is never more than the actual step cost.

Consistency implies admissibility (but not vice versa). Consistency also guarantees that A* never needs to re-open nodes from the closed list.

Properties Summary

PropertyValueCondition
Complete?YesFinite branching factor
Optimal?Yesh(n)h(n) admissible
Time complexityO(bd)O(b^d) worst caseSame as BFS in worst case
Space complexityO(bd)O(b^d)Keeps all nodes in memory

Where bb = branching factor, dd = depth of the shallowest goal.

Worst Case vs Typical Case

In the worst case (e.g., h(n)=0h(n) = 0), A* degenerates to Dijkstra's and explores everything. But with a good heuristic, A* can be exponentially faster — it only explores nodes where f(n)Cf(n) \leq C^* (optimal cost), pruning the rest.

How to Design Good Heuristics

Method 1: Relaxed Problems

Remove constraints from the original problem. The optimal cost in the relaxed problem is an admissible heuristic for the original.

Example: In the 8-puzzle, if you relax "only one tile can move into the blank" to "tiles can move anywhere", you get the Misplaced Tiles heuristic. If you further relax "tiles move one step at a time" to "tiles teleport", you get the Manhattan Distance heuristic.

hmisplaced(n)hmanhattan(n)h(n)h_{\text{misplaced}}(n) \leq h_{\text{manhattan}}(n) \leq h^*(n)

A more informed heuristic (higher but still h\leq h^*) dominates a less informed one and leads to fewer node expansions.

Method 2: Pattern Databases

Precompute optimal solution costs for sub-problems and store in a lookup table. The maximum of multiple pattern database heuristics is also admissible and more informed.

Dominance

If h2(n)h1(n)h_2(n) \geq h_1(n) for all nn and both are admissible, then h2h_2 dominates h1h_1. A* with h2h_2 will never expand more nodes than with h1h_1.

h1(n)h2(n)h(n)    h2 dominates h1h_1(n) \leq h_2(n) \leq h^*(n) \implies h_2 \text{ dominates } h_1

Worked Example: Step-by-Step Trace

Here's an 8-node graph. Start at S, goal is G. Heuristic values are shown in the table.

NodeSABCDEFG
h(n)h(n)107543210

The edge costs are shown on the graph below. Click through each step to see how A* expands nodes:

A* TraceStep 0 / 8
Start
Goal
Current
Frontier
Explored
Optimal Path

Start: Open list = {S}, g(S)=0, h(S)=10, f(S)=0+10=10

Key Observations from This Trace

  1. Tie at step 1: After expanding S, nodes A, B, C all have f=10f = 10. We break ties by preferring lower gg (closer to start → more progress made), so A is expanded first.
  2. Path update at step 3: When B is expanded, node E already has g=9g = 9 (via A), but the path S→B→E gives g=8g = 8 (cheaper). A* updates E's gg-value. This is why we check the open list before skipping.
  3. Suboptimal path rejection: The path S→A→E→G costs 3+6+4=133 + 6 + 4 = 13, but A* finds S→A→D→G costs 3+4+5=123 + 4 + 5 = 12. Both are explored but only the optimal one is returned.
  4. Goal not expanded immediately: Even after G enters the open list, A* continues expanding nodes with f<f(G)=12f < f(G) = 12 (like E with f=10f = 10). Only when G has the lowest ff is it selected — guaranteeing optimality.

Exam Practice: Harder Question

This is a Stanford/IIT-level question. The graph has a non-trivial structure where the "obvious" greedy path is not optimal. Try to solve it yourself before clicking "Show Solution".

Given: Find the shortest path from P to Z using A* search.

NodePQRTUVWZ
h(n)h(n)96743250
Exam PracticeTry it yourself first!

Work through A* step-by-step on paper, then click "Show Solution" to verify your answer.

Why This Question Is Tricky

  1. The greedy trap: Going P→Q seems attractive because h(Q)=6h(Q) = 6 is lower than h(R)=7h(R) = 7, but the optimal path goes through R first.
  2. Multiple paths to same node: Both Q and R can reach U. A* must correctly determine that R→U is cheaper than Q→T→U.
  3. Path cost verification: P→Q→T→V→Z = 4+3+3+3 = 13 vs P→R→U→V→Z = 2+5+2+3 = 12. The first path looks "more direct" in the graph but is 1 unit more expensive.
QuizIf h(n) = 0 for all nodes, what does A* reduce to?
QuizIf h(n) = h*(n) (the true optimal cost) for all n, how many nodes does A* expand on the optimal path?
QuizA heuristic h₁(n) = 2 and h₂(n) = 5 are both admissible. Which is more informed?