A* Search — Heuristic Shortest Path for Grid Problems

Sanjeev SharmaSanjeev Sharma
2 min read

Advertisement

A* vs Dijkstra

Dijkstra explores in order of g(n) (cost from start). A* explores in order of f(n) = g(n) + h(n) where h(n) estimates remaining cost.

Good heuristics for grids:

  • Manhattan distance: |dx| + |dy| (4-directional)
  • Euclidean distance: sqrt(dx²+dy²) (any direction)
  • Chebyshev: max(|dx|,|dy|) (8-directional)

Solutions

Python

import heapq

def astar(grid, start, end):
    rows, cols = len(grid), len(grid[0])
    sr, sc = start
    er, ec = end

    def h(r, c):
        return abs(r-er) + abs(c-ec)

    heap = [(h(sr,sc), 0, sr, sc)]
    dist = {(sr,sc): 0}

    while heap:
        f, g, r, c = heapq.heappop(heap)
        if (r,c) == (er,ec): return g
        if g > dist.get((r,c), float('inf')): continue
        for dr,dc in [(-1,0),(1,0),(0,-1),(0,1)]:
            nr,nc = r+dr,c+dc
            if 0<=nr<rows and 0<=nc<cols and grid[nr][nc]!=1:
                ng = g+1
                if ng < dist.get((nr,nc), float('inf')):
                    dist[(nr,nc)] = ng
                    heapq.heappush(heap, (ng+h(nr,nc), ng, nr, nc))
    return -1

JavaScript

function astar(grid, [sr,sc], [er,ec]) {
    const h=(r,c)=>Math.abs(r-er)+Math.abs(c-ec);
    const dist=new Map([[sr+','+sc,0]]);
    const heap=[[h(sr,sc),0,sr,sc]];
    const pop=()=>{let mi=0;for(let i=1;i<heap.length;i++)if(heap[i][0]<heap[mi][0])mi=i;return heap.splice(mi,1)[0];};
    while(heap.length){
        const[f,g,r,c]=pop();
        if(r===er&&c===ec)return g;
        if(g>dist.get(r+','+c))continue;
        for(const[dr,dc]of[[-1,0],[1,0],[0,-1],[0,1]]){
            const[nr,nc]=[r+dr,c+dc];
            if(nr>=0&&nr<grid.length&&nc>=0&&nc<grid[0].length&&grid[nr][nc]!==1){
                const ng=g+1,k=nr+','+nc;
                if(ng<(dist.get(k)??Infinity)){dist.set(k,ng);heap.push([ng+h(nr,nc),ng,nr,nc]);}
            }
        }
    }
    return -1;
}

Java

import java.util.*;
public int astar(int[][] grid, int[] s, int[] e) {
    int R=grid.length,C=grid[0].length,er=e[0],ec=e[1];
    int[][] dist=new int[R][C]; for(int[]r:dist)Arrays.fill(r,Integer.MAX_VALUE);
    dist[s[0]][s[1]]=0;
    PriorityQueue<int[]> pq=new PriorityQueue<>(Comparator.comparingInt(a->a[0]));
    pq.offer(new int[]{Math.abs(s[0]-er)+Math.abs(s[1]-ec),0,s[0],s[1]});
    int[][]dirs={{-1,0},{1,0},{0,-1},{0,1}};
    while(!pq.isEmpty()){
        int[]top=pq.poll(); int g=top[1],r=top[2],c=top[3];
        if(r==er&&c==ec)return g;
        if(g>dist[r][c])continue;
        for(int[]d:dirs){int nr=r+d[0],nc=c+d[1];
            if(nr>=0&&nr<R&&nc>=0&&nc<C&&grid[nr][nc]!=1&&g+1<dist[nr][nc]){
                dist[nr][nc]=g+1;pq.offer(new int[]{g+1+Math.abs(nr-er)+Math.abs(nc-ec),g+1,nr,nc});}}
    }
    return -1;
}

C++

#include <queue>
#include <vector>
#include <tuple>
using namespace std;
int astar(vector<vector<int>>&grid,pair<int,int>s,pair<int,int>e){
    int R=grid.size(),C=grid[0].size(),er=e.first,ec=e.second;
    vector<vector<int>> dist(R,vector<int>(C,INT_MAX));
    auto h=[&](int r,int c){return abs(r-er)+abs(c-ec);};
    priority_queue<tuple<int,int,int,int>,vector<tuple<int,int,int,int>>,greater<>> pq;
    dist[s.first][s.second]=0; pq.push({h(s.first,s.second),0,s.first,s.second});
    int dirs[]={-1,0,1,0,-1};
    while(!pq.empty()){
        auto[f,g,r,c]=pq.top();pq.pop();
        if(r==er&&c==ec)return g; if(g>dist[r][c])continue;
        for(int d=0;d<4;d++){int nr=r+dirs[d],nc=c+dirs[d+1];
            if(nr>=0&&nr<R&&nc>=0&&nc<C&&grid[nr][nc]!=1&&g+1<dist[nr][nc]){
                dist[nr][nc]=g+1;pq.push({g+1+h(nr,nc),g+1,nr,nc});}}
    }
    return -1;
}

C

/* C: same min-heap approach, Manhattan heuristic with int arithmetic */

Advertisement

Sanjeev Sharma

Written by

Sanjeev Sharma

Full Stack Engineer · E-mopro