Artificial Intelligence

A* Search Algorithm in AI: The Complete Guide

A* is the informed search every AI course reaches for once breadth first search runs out of road. This guide covers the evaluation function f(n) = g(n) + h(n), the 1968 paper that introduced it, admissible and consistent heuristics, the optimality proof, the complexity, and the places A* quietly goes wrong.

20 Min Read Updated: September 2026 Intermediate Level
Mohammed Islam Hadjoudj
Mohammed Islam Hadjoudj
Expert Operations Research Engineer

1. What is the A* search algorithm?

A* (pronounced "A star") is a best first graph search algorithm that finds a minimum cost path from a start node to a goal node. In artificial intelligence it is classified as an informed search: unlike breadth first search or uniform cost search, which only know what has already been paid, A* also uses an estimate of what still lies ahead.

Every node A* keeps on its frontier carries three numbers:

The algorithm repeatedly expands the frontier node with the smallest f. That single rule produces the property A* is famous for: if the heuristic never overestimates the real remaining cost, the first goal node A* removes from the frontier lies on an optimal path.

Nothing in that definition mentions grids or video games. A* is defined on any weighted graph with non negative edge costs. The graph can be a road network, a tile map, the state space of a sliding tile puzzle, a robot's configuration space, or the set of partial plans inside an automated planner. Grid pathfinding is simply the picture that is easiest to draw, and it is covered in the companion guide on A* pathfinding on grids.

Three copies of the same grid map, with a patch of slow terrain costing 6 per cell between the start and the goal. On the left, f equals g with h set to zero, labelled uniform cost search and Dijkstra: 196 cells shaded, path cost 22, optimal. In the middle, f equals g plus h, labelled A star: 95 cells shaded, path cost 22, optimal. On the right, f equals h alone, labelled greedy best first search: only 17 cells shaded, but the path drives straight through the slow terrain and costs 46 instead of 22.
A* is less a separate invention than a dial. Set h to zero and you get Dijkstra, which is optimal but explores twice as much. Drop g and you get greedy best first search, which explores almost nothing and returns a path costing 46 where 22 was available. Keep both and you get the informed middle.

2. Where A* came from: Hart, Nilsson and Raphael (1968)

Peter E. Hart, Nils J. Nilsson and Bertram Raphael, working at the Stanford Research Institute, published "A Formal Basis for the Heuristic Determination of Minimum Cost Paths" in IEEE Transactions on Systems Science and Cybernetics, volume 4, issue 2, pages 100 to 107, July 1968. The practical motivation was Shakey, the SRI mobile robot, which had to plan its own route through a suite of rooms rather than follow a scripted track.

Two contributions from that paper still define the subject:

  1. It separated the search procedure from the knowledge inside it. The same loop becomes a different algorithm depending on which h you hand it. Search strategy became a parameter rather than a design decision.
  2. It proved the guarantees. If h never overestimates the true remaining cost, the procedure returns a minimum cost path. A companion result showed that no other algorithm with access to the same heuristic information can expand fewer nodes and still guarantee optimality.

The name is a piece of notation that stuck. Nilsson had described an earlier family of procedures labelled A1, A2 and so on. The star marks the member of that family that uses an admissible heuristic and is therefore the optimal one. It is not an abbreviation and it does not stand for anything.

A 1972 note in the SIGART Newsletter corrected the original optimality argument. The 1968 proof leaned on what the authors called the consistency assumption, and the correction made explicit what has to change when a heuristic is admissible but not consistent: already expanded nodes must be allowed to be reopened. That distinction is section 6 below, and it is still the single most common source of silently wrong A* implementations more than fifty years later.

If a citation is what brought you here: Hart, P. E., Nilsson, N. J., and Raphael, B. (1968). "A Formal Basis for the Heuristic Determination of Minimum Cost Paths." IEEE Transactions on Systems Science and Cybernetics 4(2), 100 to 107.

3. The evaluation function f(n) = g(n) + h(n)

Everything A* does follows from how it ranks its frontier. The evaluation function splits the estimate of a solution into a part that is known exactly and a part that is guessed:

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

g(n)   cost already paid     exact, measured along the path actually taken
h(n)   cost still to pay     estimated by the heuristic function
f(n)   total estimate        the value the priority queue sorts on

The value of that design is that one component controls the entire behaviour of the search:

Read that list as a scale of how much you trust the estimate. A* sits in the middle and spends effort in proportion to how little the heuristic knows: a sharp heuristic produces a search that looks like a straight line, a weak one produces a search that looks like Dijkstra.

4. How A* runs, step by step

The frontier is a priority queue, traditionally called OPEN, ordered by f. A map of best known g values and a map of parent pointers complete the state.

function a_star(start, goal, h):
    open   = priority queue ordered by f        # the frontier
    g      = map with default infinity          # best known cost from start
    parent = map                                # for path reconstruction

    g[start] = 0
    open.push(start, h(start))

    while open is not empty:
        current = open.pop_min()                # node with the smallest f
        if current == goal:
            return reconstruct(parent, goal)    # goal test on pop, not on generate

        for (next, cost) in edges(current):
            tentative = g[current] + cost
            if tentative < g[next]:             # a cheaper route to next
                g[next]      = tentative
                parent[next] = current
                open.push_or_update(next, tentative + h(next))

    return failure                              # the goal is unreachable

Four implementation details carry most of the subtlety:

5. A worked example on a weighted graph

Grid animations hide the arithmetic. Here is a small weighted graph, the kind used in AI courses, traced by hand from start to finish.

Six nodes: S (start), A, B, C, D and G (goal). Edge costs are road distances, and the heuristic h is a straight line distance to G, which is what makes it admissible: a straight line is never longer than a road.

Edges (undirected, cost)      Heuristic h(n)
S-A  4      A-C  5            S = 11    C = 5
S-B  3      A-D  12           A =  9    D = 3
S-G  20     B-D  7            B =  8    G = 0
C-G  6      D-G  3
A weighted graph with six nodes. S connects to A with cost 4, to B with cost 3 and directly to G with cost 20. A connects to C with cost 5 and to D with cost 12. B connects to D with cost 7. C connects to G with cost 6 and D connects to G with cost 3. Each node is labelled with its heuristic value: S is 11, A is 9, B is 8, C is 5, D is 3 and G is 0. The path S to B to D to G is highlighted as the optimal path of cost 13.
The example graph. The expensive direct edge S to G exists precisely to show why the goal test has to wait until a node is popped.

A* pops the node with the smallest f, breaking ties in favour of the smaller h, which is the standard tie break because it prefers nodes nearer the goal:

StepPoppedghfFrontier after the step
1S01111B 11, A 13, G 20
2B3811A 13, D 13, G 20
3D10313G 13 (improved from 20), A 13
4G13013goal popped, search ends

The answer is S → B → D → G at a cost of 13. Three things in that trace are worth pausing on:

6. Admissible and consistent heuristics

Two properties of h are worth distinguishing carefully, because they are often used as though they were the same thing and they are not.

Admissibility

A heuristic is admissible if it never overestimates:

h(n) <= h*(n)   for every node n
                where h*(n) is the true cheapest cost from n to a goal

Admissibility is optimism. The heuristic is allowed to be wildly wrong as long as it errs on the low side. h = 0 is admissible, which is why Dijkstra is a special case of A* rather than a rival to it.

Consistency, also called monotonicity

A heuristic is consistent if it satisfies a triangle inequality along every edge:

h(n) <= cost(n, n') + h(n')   for every edge n to n'
h(goal) = 0

In words: taking one step can never reduce your estimate by more than the step actually cost. Consistency is the stronger property. Every consistent heuristic is admissible; the reverse is false.

The practical consequence is precise. When h is consistent, f never decreases along a path, so the first time A* pops a node, its g value is already optimal. That is what licenses the familiar optimisation of putting expanded nodes into a closed set and never looking at them again.

When h is only admissible, that licence is withdrawn. A cheaper route to an already expanded node can still be discovered, and if the implementation refuses to reopen it, the final answer can be wrong. Here is a graph where exactly that happens:

A directed graph with five nodes used as a counterexample. S goes to A with cost 1 and to B with cost 2. A goes to C with cost 1 and B goes to C with cost 1. C goes to G with cost 10. Heuristic values are S equals 0, A equals 11, B equals 5, C equals 0 and G equals 0. All values are admissible, but the edge from A to C violates consistency because 11 is greater than 1 plus 0. A closed set implementation returns the path S B C G at cost 13 instead of the optimal S A C G at cost 12.
Every heuristic value here is admissible, yet an implementation that refuses to reopen closed nodes returns a path costing 13 when a path costing 12 exists.
Edges (directed, cost)     Heuristic h(n)     True cost h*(n)
S -> A   1                 S =  0             S = 12
S -> B   2                 A = 11             A = 11
A -> C   1                 B =  5             B = 11
B -> C   1                 C =  0             C = 10
C -> G  10                 G =  0             G =  0

Every h(n) <= h*(n), so the heuristic is admissible.
Edge A -> C breaks consistency: h(A) = 11 > cost(A,C) + h(C) = 1.

Trace it: A* pops S (f = 0), generating A at f = 1 + 11 = 12 and B at f = 2 + 5 = 7. It pops B (f = 7) and generates C with g = 3, f = 3. It pops C (f = 3), closes it with g = 3, and generates G at f = 13. Only then does it pop A (f = 12) and discover a route to C costing just 2. If C is closed and closed means final, that discovery is thrown away and the algorithm returns 13 instead of the optimal 12.

Two ways out, and you should pick one deliberately: prove your heuristic is consistent and keep the closed set optimisation, or write the relaxation as tentative < g[next] with no closed set check so reopening happens on its own. The reassuring news is that nearly every heuristic used in practice, including straight line distance, Manhattan distance on a uniform cost grid, and any heuristic derived from a relaxed problem, is consistent. That is precisely why this bug can survive years of use before a strange map exposes it.

7. Why A* is optimal, and when it is not

The optimality argument is short enough to hold in your head, and worth knowing because it tells you exactly which assumption to check when A* misbehaves.

Let C* be the cost of an optimal solution. Suppose A* is about to pop a goal node G2 reached along a suboptimal path, so g(G2) > C*. Since h(G2) = 0, its priority is f(G2) = g(G2) > C*. Now consider any optimal path from the start to a real optimal goal. At the moment before the pop, that path must have at least one node n sitting on the frontier, because the start was expanded and the goal was not. For that node, g(n) is the optimal cost to reach it and, because h is admissible, f(n) = g(n) + h(n) <= g(n) + h*(n) = C*. So f(n) <= C* < f(G2), and A* would have popped n first. The contradiction proves that the first goal popped is optimal.

The same reasoning gives you the conditions under which the guarantee fails:

One further result rounds out the theory. Dechter and Pearl proved in 1985 that A* is optimally efficient: no other optimal algorithm using the same heuristic information is guaranteed to expand fewer nodes. Every node with f(n) < C* must be expanded by any such algorithm, and A* expands exactly those, plus some subset of the nodes sitting exactly at f(n) = C*. There is no cleverer scheduling to be found; the only remaining lever is a better heuristic.

8. Time and space complexity

The textbook figure is O(bd), where b is the branching factor and d is the depth of the optimal solution, and that number is honest but not very informative. The sharper statement is about the heuristic's error. The number of nodes A* expands grows exponentially unless the error of the heuristic is bounded logarithmically in the true cost:

|h(n) - h*(n)| <= O(log h*(n))

Almost no heuristic used in practice meets that bar, so the honest reading is that A* is exponential in the worst case and that a good heuristic reduces the base of the exponent rather than removing it. On an explicit graph that you already hold in memory, the accounting is the same as Dijkstra's: O(E log V) with a binary heap, since every edge triggers at most one queue update. The interesting question is not the bound but how few of those V and E the search ever touches.

Memory is the constraint that actually bites. A* keeps every generated node in memory, both the frontier and the explored set, so its space complexity matches its time complexity. Real systems run out of RAM long before they run out of patience. This single fact explains the entire family of variants in section 11: iterative deepening A*, simplified memory bounded A*, and beam style truncations all exist to trade optimality or repeated work for a frontier that fits.

9. How to design a good heuristic

Since the algorithm is fixed and provably efficient, all of the engineering lives in h. There is a reliable recipe for producing one.

Relax the problem

Take the real problem, delete a constraint, and solve what is left exactly. The exact cost of a relaxed problem is always a lower bound on the real cost, so it is admissible by construction, and because a relaxed solution can be built one step at a time it is consistent as well.

The 8 puzzle is the standard illustration. A tile may move to the adjacent blank square. Delete constraints and two classic heuristics fall out:

Both are admissible. The difference in practice is dramatic: on random 8 puzzle instances requiring 12 moves, the classic comparison reported by Russell and Norvig has iterative deepening search generating on the order of 3.6 million nodes, A* with h1 generating a few hundred, and A* with h2 generating fewer than a hundred. At depth 24 the gap widens to roughly 39,000 nodes for h1 against roughly 1,600 for h2. Same algorithm, same code, different h.

Prefer a dominant heuristic

If h2(n) >= h1(n) for every node and both are admissible, then h2 dominates h1 and A* with h2 never expands more nodes. Bigger is better, right up to the ceiling of h*. This is why Manhattan distance beats misplaced tiles: it is always at least as large and never overestimates.

Combine several heuristics

If you have several admissible heuristics and none dominates the others, take their maximum. The maximum of admissible heuristics is admissible, and it dominates all of them. The cost is that you now evaluate several functions per node, so the win has to be paid for in evaluation time.

Precompute with pattern databases

For a fixed goal state, solve subproblems exhaustively in advance and store the exact costs in a table. A pattern database for a subset of the puzzle tiles gives a far stronger admissible bound than any formula. This is the same idea that makes landmark based routing work on continental road networks: precompute exact distances to a handful of landmarks, then use the triangle inequality to bound the rest.

On grids, choose the metric that matches the moves

Manhattan distance for 4 directional movement, Euclidean for any angle movement, Chebyshev or octile for 8 directional grids. Choosing a metric that allows more freedom than the movement rules do makes the heuristic inadmissible and quietly destroys the optimality guarantee. The grid pathfinding guide works through all three with worked numbers.

10. A* vs Dijkstra, UCS, greedy best first and BFS

Every one of these is the same loop with a different priority. That is the most useful way to remember them.

AlgorithmFrontier priorityOptimal?Use it when
Breadth first searchinsertion order (FIFO)Yes, if all edges cost the sameUnweighted graphs, fewest hops
Uniform cost search / Dijkstraf = gYesWeighted graph, no useful estimate available, or many goals at once
Greedy best first searchf = hNoYou need any solution fast and quality is negotiable
A*f = g + hYes, if h is admissibleSingle goal, a decent estimate exists, the path must be shortest
Weighted A*f = g + w × hWithin a factor of wA* is too slow and a bounded detour is acceptable

The intuition behind the A* against Dijkstra comparison is geometric. Dijkstra expands everything with g(n) < C*, which on an open map is a circle around the start. A* expands everything with g(n) + h(n) < C*, which is an ellipse with the start and the goal as its focal points. The sharper the heuristic, the flatter that ellipse becomes, until it collapses onto the path itself. Both return the same optimal answer; they differ only in how much of the map they had to look at to be sure. For a broader map of the family, see the overview of shortest path algorithms, and for the traversal foundations underneath all of it, BFS vs DFS.

11. Variants of A* worth knowing

Nearly every named variant is a response to one of two pressures: memory, or the need to answer before the optimum is proved.

12. Common mistakes in A* implementations

Most A* bugs do not crash. They return a path that is slightly too long, on some inputs, sometimes. These are the recurring causes:

13. Where A* is actually used

A* earns its place anywhere a best sequence of decisions has to be found in a space too large to enumerate:

The pattern is always the same. Model the problem as a graph, find an admissible lower bound on the remaining cost, and let the priority queue do the rest.

14. Frequently asked questions

Does A* always find the shortest path?

Yes, provided three conditions hold: the heuristic is admissible (it never overestimates the remaining cost), edge costs are non negative, and the implementation tests for the goal when a node is popped rather than when it is generated. If the heuristic is admissible but not consistent, the implementation must also allow already expanded nodes to be reopened. Break any one of those conditions and A* still returns a path, but it is no longer guaranteed to be the cheapest one.

What is the difference between A* and Dijkstra's algorithm?

They are the same algorithm with different priorities. Dijkstra orders its frontier by g(n), the cost already paid, so it expands outwards in every direction like a growing circle. A* orders by f(n) = g(n) + h(n), so the region it expands is an ellipse stretched from the start towards the goal. Setting h(n) = 0 turns A* into Dijkstra exactly. Dijkstra is the better choice when no useful estimate exists or when you need distances to many destinations at once; A* is the better choice for a single destination with a decent estimate available.

What is the difference between an admissible and a consistent heuristic?

Admissible means h(n) is never greater than the true remaining cost h*(n). Consistent, also called monotone, is the stronger triangle inequality h(n) <= cost(n, n') + h(n') for every edge, with h(goal) = 0. Every consistent heuristic is admissible, but not the reverse. Consistency matters because it guarantees that the first time A* pops a node, its g value is already optimal, which is what makes a closed set safe. With a merely admissible heuristic, a cheaper route to a closed node can still appear later and the node must be reopened.

What is the time and space complexity of A*?

In the worst case both are O(b^d), exponential in the depth of the solution. The number of expansions stays polynomial only when the heuristic error is bounded logarithmically, which almost never happens in practice, so a better heuristic reduces the base of the exponent rather than removing it. On an explicit graph already in memory, A* behaves like Dijkstra at O(E log V) with a binary heap. Space is the practical limit: A* stores every node it generates, which is why memory bounded variants such as IDA* and SMA* exist.

Why is it called A*, and is it an AI algorithm or a graph algorithm?

The name comes from Nilsson's earlier family of procedures named A1, A2 and so on; the star marks the member of that family that uses an admissible heuristic and is therefore optimal. It is both an AI algorithm and a graph algorithm. It is taught in AI courses as the canonical informed search method, and it is a shortest path algorithm on weighted graphs. It is not machine learning: nothing is trained and no data is fitted. The knowledge lives entirely in the heuristic function you supply, although a learned model can perfectly well be used to supply one.

Is A* complete, and what happens if no path exists?

A* is complete on a graph with a finite branching factor whose edge costs are bounded below by some positive value: if a solution exists it will be found. When no path exists, the frontier empties after the entire reachable component has been expanded and the algorithm reports failure. That failure case is the expensive one, since proving that nothing exists means exploring everything reachable, which is a good reason to add a connectivity precheck on large maps.

See the frontier move

Run Dijkstra on your own graph in the interactive visualizer. It is A* with h set to zero, so what you watch flooding outwards is exactly the work a good heuristic removes.

Launch the visualizer

Further Exploration

Master Pathfinding

Reading about A* is good, but building mazes and watching the algorithm solve them is better. Draw walls, set start and end points, and watch how different heuristics change the search pattern in real-time.

Launch A* Visualizer