A* Search Algorithm Theory Guide

Try the A* Search Algorithm Solver →
Intermediate12 min readLast Updated June 26, 2026
Prerequisites:Graphs and Trees, Heuristics
A* AlgorithmA star searchPathfinding CalculatorHeuristic Searchg(n) + h(n) solverOpen List Closed ListShortest Path

Think about navigating a new city on foot. You don't walk in every direction at once (that's inefficient), and you don't pick one road and hope for the best (that's a gamble). Instead, you use two things: the road you’ve already walked and your intuition of which way the destination lies. A* Search is simply that combination—actual experience plus a smart guess—formalized into the industry-standard algorithm for finding the shortest path.

  • It moves with purpose, not desperation: Unlike blind search algorithms that wander aimlessly, A* uses its 'intuition' to focus on the direction of the goal. It prioritizes nodes that look most promising, effectively ignoring vast regions of the map that are clearly leading the wrong way.
  • It balances the past against the future: At every single node, A* adds together two simple values: the exact cost spent to get there, and a 'best guess' estimate of how much further it is to the end. This total score tells the algorithm exactly which path to explore next.
  • It is the gold standard for efficiency and accuracy: As long as your 'best guess' doesn't overestimate the true distance, A* is mathematically guaranteed to find the absolute shortest path. It gives you the perfect answer without needing to search every single possibility.

Whether it's a GPS calculating your commute or a game character navigating a dungeon, A* is the reason AI finds the perfect path without wasting time.

How to Trace A* by Hand

1

Initialize your two lists before touching anything else. Draw two columns on your exam paper: `openList` and `closedList`. The `openList` holds nodes that have been discovered but not yet fully processed — these are your candidates for the next move. The `closedList` holds nodes that have already been processed and will never be revisited. Place the start node in the `openList` only. Calculate its values immediately: g(n)=0g(n) = 0 (you have not travelled anywhere yet), h(n)h(n) = the heuristic value given in the question, and f(n)=g(n)+h(n)f(n) = g(n) + h(n). Write all three values next to the node.

2

Select the node in the `openList` with the lowest f(n)f(n) value. Scan every node currently in your `openList` and pick the one with the smallest f(n)f(n). This is the node A* believes is on the most promising path to the goal. If two nodes share the same f(n)f(n) value, either can be chosen — pick the one that appears first or is alphabetically earlier to stay consistent with your exam's expected output.

3

Move the selected node to the `closedList` and expand its neighbours. Cross it off the `openList` and write it into the `closedList`. A node in the `closedList` is permanently settled — do not touch it again. Now look at every neighbour of this node. For each neighbour, calculate three values: g(n)g(n) = the selected node's gg value plus the edge cost to reach this neighbour; h(n)h(n) = the heuristic estimate given for this neighbour; f(n)=g(n)+h(n)f(n) = g(n) + h(n). Write all three values down before making any decisions.

4

Apply the update rules for each neighbour — this is where most exam marks are lost. For each calculated neighbour, check two conditions in order. First: if the neighbour is already in the `closedList`, skip it entirely — it has been fully processed and cannot be improved. Second: if the neighbour is not in the `openList` yet, add it now with the calculated f(n)f(n), g(n)g(n), and h(n)h(n) values. Third: if the neighbour is already in the `openList`, compare your newly calculated g(n)g(n) against the g(n)g(n) it already has. If your new g(n)g(n) is lower, you found a cheaper path — update its values. If your new g(n)g(n) is equal or higher, leave it unchanged.

5

Repeat until the goal node is moved to the `closedList`. Keep selecting the lowest f(n)f(n) node from the `openList`, moving it to the `closedList`, and expanding its neighbours. Stop the moment the goal node itself gets selected from the `openList` and moved to the `closedList` — not when it is first discovered and added to the `openList`. That distinction is a classic exam trap. The final shortest path cost is the g(n)g(n) value of the goal node at termination.

The A* Evaluation Function

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

Breaking Down the Formula & The Admissibility Trap

  • f(n)f(n) — The Decider: This is the total estimated cost of the cheapest path that passes through node nn. A* is completely driven by this single number — at every step, it reaches into the `openList` and grabs whichever node has the lowest f(n)f(n). Think of it as a priority score. The lower it is, the more promising the node looks. Everything else in the algorithm exists to calculate this number accurately.
  • g(n)g(n) — The Exact Cost So Far: This is the real, verified cost paid to travel from the start node to node nn along the current best known path. There is no estimation here — g(n)g(n) is pure history. Every edge you crossed had a cost, and g(n)g(n) is the running total. It only ever increases as you move deeper into the graph.
  • h(n)h(n) — The Heuristic Guess: This is the estimated cost to travel from node nn to the goal. It is a prediction, not a guarantee. A common choice is the straight-line distance to the goal — cheap to calculate and directionally sensible. A good h(n)h(n) points the algorithm toward the goal without lying about how far it really is.
  • The Admissibility Rule — The Most Tested Theory Question in A*: For A* to guarantee it finds the shortest path, h(n)h(n) must be admissible — meaning it must never overestimate the true remaining cost to the goal. It can be zero. It can be exact. It can underestimate. But the moment it overestimates even once, A* loses its optimality guarantee and may return a suboptimal path without warning. If your professor asks 'under what condition does A* always find the optimal path?', write this: h(n)h(n) must be admissible — it must never overestimate the true cost to the goal.

Solved Example: Tracing A* on Paper

Draw this graph on your paper before reading the steps. Nodes: S (Start), A, B, C, G (Goal). Edges and their costs: S→A = 1, S→B = 1, A→B = 9, B→C = 6, B→G = 12, C→G = 5. Heuristic values (given, not calculated): h(S) = 7, h(A) = 10, h(B) = 9, h(C) = 5, h(G) = 0. Now draw two columns on your paper and label them `openList` and `closedList`. These two columns are your entire working space for this trace.

Step 1: Initialize the Start Node S

Place S in the `openList`. Calculate its evaluation score: g(S)=0g(S) = 0 (you have not moved yet) and h(S)=7h(S) = 7 (given). Therefore f(S)=g(S)+h(S)=0+7=7f(S) = g(S) + h(S) = 0 + 7 = 7. Write S(7) in your `openList`. Your `closedList` is empty. There is only one candidate, so the first selection is trivial — but the initialization step itself is always worth marks, so never skip it.

Step 2: Pop S — Expand Neighbours A and B

S has the lowest f(n)f(n) in the `openList` (it is the only one). Move S from the `openList` to the `closedList`. Now expand S's neighbours. For A: g(A)=g(S)+1=0+1=1g(A) = g(S) + 1 = 0 + 1 = 1, h(A)=10h(A) = 10, so f(A)=1+10=11f(A) = 1 + 10 = 11. For B: g(B)=g(S)+1=0+1=1g(B) = g(S) + 1 = 0 + 1 = 1, h(B)=9h(B) = 9, so f(B)=1+9=10f(B) = 1 + 9 = 10. Add both to the `openList`. Your `openList` now holds A(11) and B(10). Your `closedList` holds S.

Step 3: Pop B — Expand Neighbours C and G

Scan the `openList`: B has the lowest f(n)f(n) at 10. Move B to the `closedList`. Expand B's neighbours. For C: g(C)=g(B)+6=1+6=7g(C) = g(B) + 6 = 1 + 6 = 7, h(C)=5h(C) = 5, so f(C)=7+5=12f(C) = 7 + 5 = 12. For G: g(G)=g(B)+12=1+12=13g(G) = g(B) + 12 = 1 + 12 = 13, h(G)=0h(G) = 0, so f(G)=13+0=13f(G) = 13 + 0 = 13. Add both to the `openList`. Your `openList` now holds A(11), C(12), G(13). Your `closedList` holds S, B.

Step 4: Pop A — The Exam Trap

Scan the `openList`: A is now the lowest at f(A)=11f(A) = 11. Move A to the `closedList`. Expand A's neighbours — its only neighbour is B, via an edge costing 9. The new g(B)g(B) via this path would be g(A)+9=1+9=10g(A) + 9 = 1 + 9 = 10. But before you calculate anything further, check your `closedList` — B is already there. The rule is immediate and unconditional: if a neighbour is in the `closedList`, skip it. Do not update it. Do not add it back. This is the exact step where examiners separate students who understand the algorithm from students who are just doing arithmetic.

Step 5: Pop C — Update G and Terminate

Scan the `openList`: C is the lowest at f(C)=12f(C) = 12. Move C to the `closedList`. Expand C's only neighbour: G. The new g(G)g(G) via this path is g(C)+5=7+5=12g(C) + 5 = 7 + 5 = 12, so f(G)=12+0=12f(G) = 12 + 0 = 12. Now check — G is already in the `openList` with a score of 13. Since 12<1312 < 13, you found a cheaper path to G. Update G's score in the `openList` to f(G)=12f(G) = 12. Now scan the `openList` one final time: G(12) is the lowest. Pop G into the `closedList`. The goal node has been settled — the algorithm terminates. The optimal path cost is g(G)=12g(G) = 12, and the path is S → B → C → G.

See the Interactive Solver in Action

Now that you can trace it by hand, use the solver to watch A* work in real time. See the `openList` and `closedList` build dynamically, watch g(n)g(n) costs update as cheaper paths are discovered, and follow the algorithm as it locks in the shortest path step by step.

Rules & Common Mistakes

  • Exam Trap: Do Not Stop When the Goal Enters the Open List
    This is the single most common termination mistake in A* exams. The moment students see the Goal node appear in the `openList`, they circle it and call it done. That is wrong — and it will cost you marks every time. A* only guarantees the shortest path when the Goal node is popped from the `openList` and moved into the `closedList`. Until that happens, a cheaper path to the Goal might still be discovered through another route. Wait for the pop, not the discovery.
  • Exam Trap: Always Update Existing Nodes in the Open List
    When you expand a neighbour and find it is already sitting in the `openList`, do not skip it. This is where the algorithm does its most important work. Compare your newly calculated g(n)g(n) against the g(n)g(n) that node currently holds. If your new path is cheaper, cross out the old f(n)f(n) score on your paper and write the updated one. If your new path is equal or more expensive, leave it alone. Ignoring this check means you may trace an entire path to the goal using a suboptimal route — and your final answer will be wrong.
  • Exam Trap: Nodes in the Closed List Are Permanently Dead — Never Touch Them
    The moment a node moves to the `closedList`, it is finalized. If you later discover what appears to be a cheaper path to a node that is already in the `closedList`, do not recalculate it, do not move it back to the `openList`, and do not update its score. Ignore it completely and move on. With an admissible heuristic, A* guarantees that the first time a node is settled into the `closedList`, the path used to get there is already the optimal one. Revisiting closed nodes is always wasted work — and in an exam context, it signals a fundamental misunderstanding of the algorithm.
  • Pro Tip: Two Nodes With the Same f(n)f(n) — Do Not Freeze, Just Pick Alphabetically
    Tie-breaking is the moment students stall and waste precious exam time. If two nodes in your `openList` share the exact same f(n)f(n) value, A* will find the optimal path regardless of which one you expand first — the final shortest-path cost is guaranteed to be identical either way. The only thing that changes is the order of your trace. To stay consistent with standard grading rubrics, break ties alphabetically. Pick the node whose label comes first in the alphabet, expand it, and keep moving. Do not overthink it.

Strengths, Weaknesses & When To Use It

When to use it:A* is the undisputed standard for pathfinding in the real world — GPS navigation, video game character movement, and robot motion planning all rely on it. On an exam, reach for A* whenever the question gives you a graph with heuristic values and asks for the guaranteed shortest path. If the question does not require the shortest path and just wants any path found quickly, Greedy Best-First Search will get there faster with less work. If the question explicitly constrains memory or asks about space-efficient alternatives, A* is the wrong choice — its memory usage is its critical weakness, and that is exactly what IDA* was designed to fix.

Advantages

  • Optimal and Complete — Mathematically Guaranteed: As long as the heuristic h(n)h(n) is admissible (never overestimates the true remaining cost), A* is guaranteed to find the absolute shortest path every single time. It is also complete — if a path to the goal exists, A* will find it. No other widely-used pathfinding algorithm combines both guarantees with the efficiency A* delivers. This is why it is the gold standard for accuracy in any search problem where the shortest path actually matters.
  • Directed Efficiency — Smarter Than Dijkstra: Dijkstra's algorithm expands outward from the start like a water spill, flooding every direction equally with no sense of where the goal is. A* uses h(n)h(n) to pull the search toward the goal, ignoring large regions of the graph that are clearly leading the wrong way. On a large map, this difference is enormous — A* evaluates a fraction of the nodes Dijkstra would touch while still returning the identical shortest path.

Disadvantages

  • The Memory Hog — Space Complexity is O(bd)O(b^d): A* keeps every node it has ever generated in memory across both the `openList` and `closedList`. As the branching factor bb and solution depth dd grow, memory consumption explodes exponentially. On large, complex graphs, A* does not fail because it runs out of time — it fails because it runs out of RAM. This is the exact limitation that motivated memory-efficient variants like IDA*, which trades memory for repeated computation.
  • Only As Smart As Its Heuristic — Garbage In, Garbage Out: A* is entirely dependent on the quality of h(n)h(n). If h(n)h(n) overestimates even once, the admissibility guarantee breaks and A* may confidently return a suboptimal path with no warning. If h(n)h(n) is trivially weak — for example, h(n)=0h(n) = 0 for every node — A* loses all directional guidance and degrades into standard Dijkstra, expanding nodes in every direction and completely wasting its heuristic advantage. The algorithm is only as intelligent as the estimate you feed it.

A* Search vs. Greedy Best-First Search

A* and Greedy Best-First Search both use a heuristic h(n)h(n) to steer toward the goal — but they have radically different philosophies about what that means. Greedy is short-sighted: it only cares about which node looks closest to the goal right now, with no memory of how expensive it was to get there. A* is balanced: it weighs the real cost of the journey so far against the estimated cost remaining. A* is not a different algorithm from Greedy — it is Greedy Search that learned to remember the past.

  • The Formula: Greedy Search evaluates nodes using only f(n)=h(n)f(n) = h(n) — it looks exclusively at the estimated future cost and asks 'which node appears closest to the goal?' A* evaluates nodes using f(n)=g(n)+h(n)f(n) = g(n) + h(n) — it combines the exact past cost with the estimated future cost and asks 'what is the true total cost of this entire journey?' One number versus two is the entire difference between reckless and reliable.
  • The Mindset — Taking the Bait: Greedy blindly chases whichever node has the smallest h(n)h(n), even if reaching that node required crossing a brutally expensive edge. It sees a node that looks close to the goal and sprints toward it, ignoring the cost of the road it just crossed. A* cannot be baited this way — the g(n)g(n) term penalizes expensive paths regardless of how close they appear to the goal. An attractively-positioned but expensively-reached node will always lose out to a cheaper, well-rounded alternative.
  • The Optimality Guarantee: A* with an admissible heuristic is mathematically guaranteed to find the shortest path every time. Greedy Search carries no such guarantee — it frequently returns a suboptimal path because it commits to whatever looks promising early and never reconsiders. A node that appeared slightly further away but offered a far cheaper overall route gets ignored entirely. Greedy is fast. Greedy is also frequently wrong.
  • What Your Exam Is Actually Testing: When a professor asks you to trace both algorithms on the same graph, that graph has been deliberately designed with a trap — a visually attractive path that is actually more expensive than the optimal route. They are testing whether you know that Greedy will take the bait, follow the low h(n)h(n) values straight into an expensive dead end, and return a suboptimal answer. A* will calculate g(n)g(n) at every step, reject the expensive shortcut, and find the true shortest path. If your Greedy and A* traces return the same path on an exam question, re-check your work — the question was almost certainly designed so they diverge.

Implementation Pseudocode

// A* maintains two lists throughout the entire search
// openList  = nodes discovered but not yet finalized — sorted by f(n) = g(n) + h(n)
// closedList = nodes fully processed — their shortest path is guaranteed and locked in

function aStar(startNode, goalNode):

    // ── INITIALIZATION ──────────────────────────────────────────────────

    // Set the start node's g(n) to 0 — no travel cost yet
    // Every other node's g(n) starts at Infinity (undiscovered)
    startNode.g = 0
    startNode.h = heuristic(startNode, goalNode)
    startNode.f = startNode.g + startNode.h

    // Add the start node to the openList as the first candidate
    openList  = [startNode]
    closedList = []


    // ── MAIN LOOP ────────────────────────────────────────────────────────

    while openList is not empty:

        // Always pop the node with the lowest f(n) value
        // This is what makes A* informed — it always chases the most promising path
        current = node in openList with the lowest f(n)
        remove current from openList

        // EXAM TRAP — Goal check happens HERE, after popping, not when generating neighbours
        // Checking on discovery would not guarantee the shortest path had been found yet
        if current == goalNode:
            return reconstruct_path(current)  // trace parent pointers back to start

        // Move current to the closedList — its shortest path is now finalized
        add current to closedList


        // ── EXPAND NEIGHBOURS ───────────────────────────────────────────

        for each neighbour in getNeighbours(current):

            // EXAM TRAP — Nodes in the closedList are permanently dead
            // A* with an admissible heuristic guarantees their path was already optimal
            // Do not recalculate, do not re-add, do not touch them
            if neighbour is in closedList:
                continue

            // Calculate the g(n) cost to reach this neighbour via current
            tentative_g = current.g + edgeCost(current, neighbour)

            if neighbour is not in openList:

                // Newly discovered node — calculate all three values and add it
                neighbour.g = tentative_g
                neighbour.h = heuristic(neighbour, goalNode)
                neighbour.f = neighbour.g + neighbour.h
                neighbour.parent = current
                add neighbour to openList

            // EXAM TRAP — Only update if the new path is strictly cheaper
            // Equal or more expensive paths are discarded — the existing entry stays
            else if tentative_g < neighbour.g:

                // Found a cheaper route to a node already in the openList
                // Cross out its old scores and overwrite with the improved values
                neighbour.g = tentative_g
                neighbour.f = neighbour.g + neighbour.h
                neighbour.parent = current

    // openList is empty and goal was never popped — no path exists
    return failure


// ── INITIAL CALL ─────────────────────────────────────────────────────
// aStar(startNode, goalNode)

Time & Space Complexity

ScenarioTime ComplexitySpace ComplexityNotes
Worst Case (Useless Heuristic)O(bd)O(b^d)O(bd)O(b^d)Here bb is the branching factor (neighbours per node) and dd is the depth of the optimal solution. When h(n)=0h(n) = 0 everywhere, A* receives zero directional guidance and degrades into Dijkstra's algorithm — expanding outward in every direction equally. Both time and space blow up exponentially. The critical exam point: space is the real killer here, not time. A* stores every generated node in the `openList` or `closedList`. On a large graph, the algorithm does not slow down and give you a warning — it silently fills your RAM and crashes. This is why O(bd)O(b^d) space complexity is considered A*'s fundamental limitation, and why memory-efficient variants like IDA* exist.
Best Case (Perfect Heuristic)O(d)O(d)O(d)O(d)If h(n)h(n) is perfectly accurate — meaning it returns the exact true cost to the goal at every node — A* behaves like a heat-seeking missile. It expands zero wrong nodes, follows the optimal path directly from start to goal, and never gets distracted by dead ends or suboptimal branches. Both time and space reduce to O(d)O(d), scaling only with the depth of the solution. This best case is theoretical in most real problems, but it is the intuition behind why a good heuristic matters so much — the closer h(n)h(n) is to the true cost, the closer A* gets to this ideal behaviour.
Average Case (Realistic Heuristic)O(bd)O({b^*}^d)O(bd)O({b^*}^d)In practice, heuristics are neither useless nor perfect. A realistic h(n)h(n) — like straight-line distance on a road map — shrinks the effective branching factor from bb down to bb^*, where b<bb^* < b. Instead of expanding a massive circle in all directions, A* explores a narrow cone aimed at the goal, skipping large regions of the graph that clearly lead the wrong way. The better the heuristic, the smaller bb^* becomes, and the closer performance gets to the best case. On an exam, if a question asks why a stronger heuristic improves A* performance, the answer is this: it reduces the effective branching factor, which cuts the exponential growth at the base.

Summary

A* is not magic — it is just Dijkstra's algorithm with a compass, or Greedy Search with a memory. Every concept here reduces to one balancing act: weighing the exact past cost, g(n)g(n), against the estimated future cost, h(n)h(n). The `openList`, `closedList`, and f(n)=g(n)+h(n)f(n) = g(n) + h(n) priority score are simply mechanical tools to execute this balance without falling into expensive traps. If you can initialize your lists correctly, wait for the goal to pop before terminating, update cheaper g(n)g(n) paths, and permanently ignore the `closedList` — you have genuinely mastered A*. You are no longer just blindly tracing a graph on an exam; you understand exactly how AI navigates the real world.

A* Exam Questions Students Always Get Wrong

  • Why can't I stop the algorithm the second the Goal node appears in my openList?

    Because appearing in the `openList` only means the goal has been discovered — not that the cheapest path to it has been found yet. A cheaper route to the goal might still be sitting undiscovered in another part of the graph, waiting to be expanded. You must wait until the goal node is popped from the `openList` — meaning it has the lowest f(n)f(n) of all remaining candidates — before terminating. Stopping on discovery is the single most common reason students lose marks on A* trace questions.

  • What exactly happens if my heuristic overestimates the true distance to the goal?

    The admissibility guarantee breaks immediately, and A* is no longer guaranteed to return the shortest path. An overestimating h(n)h(n) inflates the f(n)f(n) score of certain nodes, causing A* to avoid exploring them — even if they sit on the true optimal route. The algorithm may confidently return a suboptimal path with no indication that anything went wrong. On an exam, if asked to justify why a heuristic is valid, you must state that it never overestimates — that is the definition of admissibility.

  • I found a new path to a node already in my openList, but the new g(n)g(n) is higher. Do I update it?

    No — leave it completely untouched. You only ever update an existing `openList` node if the newly calculated g(n)g(n) is strictly lower than the value it currently holds, meaning you found a genuinely cheaper route. If the new g(n)g(n) is equal or higher, the existing entry is already better and the new path is irrelevant. Overwriting with an equal or worse score is a marking error that cascades into every downstream calculation.

  • What if two nodes in the openList have the exact same f(n)f(n) score? Which one do I pop first?

    Mathematically, it does not matter — A* will find the same optimal path cost regardless of which tied node you expand first. The final answer is guaranteed to be identical either way. On an exam, however, always break ties alphabetically — choose node A before node B, B before C, and so on. This keeps your trace consistent with the expected output on your professor's grading rubric and eliminates any ambiguity in your working.

  • My professor gave a graph where h(n)=0h(n) = 0 for every single node. Is this a trick question?

    It is not a trick — it is a test of whether you understand what the heuristic actually does. When h(n)=0h(n) = 0 everywhere, A* loses all directional guidance and makes every decision based purely on g(n)g(n), the exact cost so far. This means A* degrades exactly into Dijkstra's algorithm, expanding nodes in order of their true distance from the start with no goal-seeking behaviour at all. Trace it normally, and if your exam asks you to comment on it, write: with h(n)=0h(n) = 0, A* becomes an uninformed search equivalent to Uniform Cost Search.

Core University Curriculum

This algorithm and its manual calculation methods are foundational requirements in leading Computer Science and Software Engineering programs worldwide. You will find this topic heavily featured in the syllabi of these standard AI courses:

Explore Related Algorithms