Python
A Star Algorithm
Pathfinding
Optimization
Algorithm Efficiency

Python - Speed up an A Star Pathfinding Algorithm

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

Introduction

A* is fast when its heuristic is good and its implementation is careful, but Python versions slow down quickly if they create too many objects or use inefficient data structures. The biggest gains usually come from a better priority queue, cheaper node bookkeeping, and a heuristic that stays admissible while pruning more of the search space.

Start With heapq

The open set should be a binary heap, not a repeatedly sorted list.

python
1import heapq
2
3open_heap = []
4heapq.heappush(open_heap, (0, (0, 0)))
5priority, node = heapq.heappop(open_heap)

If you sort a Python list or scan for the minimum on every iteration, you are wasting time in the hottest part of the algorithm.

Use Lightweight Node State

Avoid wrapping every node in a custom class unless you truly need it. Tuples, dictionaries, and arrays are usually faster and simpler in Python.

python
1import heapq
2
3
4def astar(grid, start, goal):
5    rows, cols = len(grid), len(grid[0])
6
7    def heuristic(a, b):
8        return abs(a[0] - b[0]) + abs(a[1] - b[1])
9
10    open_heap = [(heuristic(start, goal), 0, start)]
11    came_from = {}
12    g_score = {start: 0}
13    closed = set()
14
15    while open_heap:
16        _, current_g, current = heapq.heappop(open_heap)
17        if current in closed:
18            continue
19        if current == goal:
20            path = [current]
21            while current in came_from:
22                current = came_from[current]
23                path.append(current)
24            return path[::-1]
25
26        closed.add(current)
27        r, c = current
28        for nr, nc in ((r+1, c), (r-1, c), (r, c+1), (r, c-1)):
29            if not (0 <= nr < rows and 0 <= nc < cols):
30                continue
31            if grid[nr][nc] == 1:
32                continue
33
34            neighbor = (nr, nc)
35            tentative_g = current_g + 1
36            if tentative_g < g_score.get(neighbor, float("inf")):
37                g_score[neighbor] = tentative_g
38                came_from[neighbor] = current
39                f_score = tentative_g + heuristic(neighbor, goal)
40                heapq.heappush(open_heap, (f_score, tentative_g, neighbor))
41
42    return None

This structure avoids heavy per-node objects and keeps the hot loop compact.

Pick The Right Heuristic

For a four-direction grid, Manhattan distance is usually the right heuristic. For eight-direction movement with diagonal cost, a diagonal-aware heuristic is better.

A weak heuristic turns A* into something closer to Dijkstra's algorithm. A strong admissible heuristic reduces how many nodes you expand.

Reduce Repeated Work

Hot-path performance also improves when you:

  • keep blocked cells in a fast structure
  • avoid recomputing neighbors in expensive ways
  • skip stale heap entries with a closed set or best-score check
  • store dimensions and local references outside inner loops

In Python, small constant-factor reductions matter because the loop runs many times.

Consider The Map Representation

If the grid is large, representation matters. A nested Python list works, but for very big maps, compact numeric arrays such as NumPy arrays can reduce memory pressure and improve locality for some workloads.

Even without NumPy, using integers and tuples is usually better than storing rich node objects with many attributes.

Algorithmic Improvements

Past a certain point, micro-optimizations will not save a poor search strategy. Depending on the map, larger gains may come from:

  • bidirectional search
  • hierarchical pathfinding
  • jump point search on uniform-cost grids
  • precomputed navigation graphs instead of raw cell search

Those are bigger design changes, but they often beat tiny Python-level tuning.

Common Pitfalls

A common mistake is using a list for the open set and calling min() or sorting on every loop. That becomes expensive fast.

Another mistake is choosing a heuristic that overestimates the remaining cost. That can break optimality.

It is also easy to spend time on Python object design while ignoring search-space size. The fastest code is often the code that avoids exploring unnecessary nodes in the first place.

Summary

  • Use heapq for the open set.
  • Keep node bookkeeping lightweight with tuples, dicts, and sets.
  • Match the heuristic to the movement rules of the grid.
  • Avoid repeated work inside the hot loop.
  • If simple tuning is not enough, consider algorithmic upgrades such as bidirectional search or jump point search.

Related reading
Course
Intermediate
27 lessons
15 hours
DSA Fundamentals

Master algorithmic patterns and data structures through hands-on LeetCode-style problems - from arrays and hashing to dynamic programming and advanced graphs.

View the course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Data Structures & Algorithms practice on Codemia

Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.

Practice algorithms

All Rights Reserved.