How it works
Dijkstra's algorithm grows a set of finalized nodes outward from the start, always finalizing the unvisited node with the smallest known distance next (using a priority queue). It relaxes each neighbour's tentative distance as it goes.
On a grid with non-negative weights it finds the true lowest-cost path, generalizing BFS to weighted terrain.
Implementation
function dijkstraPathfind(grid, start, end, allowDiag) { const rows = grid.length; const cols = grid[0].length; const dist = Matrix(rows, cols, Infinity); const visited = new Set(); const prev = new Map(); const heap = PriorityQueue("priority"); dist[start[0]][start[1]] = 0; priorityQueuePush(heap, { priority: 0, row: start[0], col: start[1] }); while (priorityQueueSize(heap)) { const __pattern1 = priorityQueuePop(heap); const cr = __pattern1.row; const cc = __pattern1.col; const currentKey = key(cr, cc); if (visited.has(currentKey)) continue; visited.add(currentKey); visitNode(cr, cc); if (cr === end[0] && cc === end[1]) { reconstructPath(prev, start, end); return; } for (const [nr, nc] of getNeighbors(grid, cr, cc, allowDiag)) { const neighborKey = key(nr, nc); if (visited.has(neighborKey)) continue; const nd = dist[cr][cc] + stepCost(grid, cr, cc, nr, nc); if (nd < dist[nr][nc]) { dist[nr][nc] = nd; prev.set(neighborKey, [cr, cc]); priorityQueuePush(heap, { priority: nd, row: nr, col: nc }); pushFrontier(nr, nc); } } } reportNoPath(); }
def dijkstraPathfind(grid, start, end, allowDiag): rows = grid.length cols = grid[0].length dist = Matrix(rows, cols, Infinity) visited = Set() prev = Map() heap = PriorityQueue("priority") dist[start[0]][start[1]] = 0 priorityQueuePush(heap, {priority: 0, row: start[0], col: start[1]}) while priorityQueueSize(heap): __pattern1 = priorityQueuePop(heap) cr = __pattern1.row cc = __pattern1.col currentKey = key(cr, cc) if visited.has(currentKey): continue visited.add(currentKey) visitNode(cr, cc) if ((cr == end[0]) and (cc == end[1])): reconstructPath(prev, start, end) return for nr, nc in getNeighbors(grid, cr, cc, allowDiag): neighborKey = key(nr, nc) if visited.has(neighborKey): continue nd = (dist[cr][cc] + stepCost(grid, cr, cc, nr, nc)) if (nd < dist[nr][nc]): dist[nr][nc] = nd prev.set(neighborKey, [cr, cc]) priorityQueuePush(heap, {priority: nd, row: nr, col: nc}) pushFrontier(nr, nc) reportNoPath()
#include <vector> #include <algorithm> void dijkstraPathfind(int grid, int start, int end, int allowDiag) { auto rows = grid.length; auto cols = grid[0].length; auto dist = Matrix(rows, cols, Infinity); auto visited = Set(); auto prev = Map(); auto heap = PriorityQueue("priority"); dist[start[0]][start[1]] = 0; priorityQueuePush(heap, {priority: 0, row: start[0], col: start[1]}); while(priorityQueueSize(heap)) { auto __pattern1 = priorityQueuePop(heap); auto cr = __pattern1.row; auto cc = __pattern1.col; auto currentKey = key(cr, cc); if(visited.has(currentKey)) { continue; } visited.add(currentKey); visitNode(cr, cc); if(((cr == end[0]) && (cc == end[1]))) { reconstructPath(prev, start, end); return; } for(auto& [nr, nc] : getNeighbors(grid, cr, cc, allowDiag)) { auto neighborKey = key(nr, nc); if(visited.has(neighborKey)) { continue; } auto nd = (dist[cr][cc] + stepCost(grid, cr, cc, nr, nc)); if((nd < dist[nr][nc])) { dist[nr][nc] = nd; prev.set(neighborKey, [cr, cc]); priorityQueuePush(heap, {priority: nd, row: nr, col: nc}); pushFrontier(nr, nc); } } } reportNoPath(); }
public void dijkstraPathfind(int grid, int start, int end, int allowDiag) { var rows = grid.length; var cols = grid[0].length; var dist = Matrix(rows, cols, Infinity); var visited = Set(); var prev = Map(); var heap = PriorityQueue("priority"); dist[start[0]][start[1]] = 0; priorityQueuePush(heap, {priority: 0, row: start[0], col: start[1]}); while(priorityQueueSize(heap)) { var __pattern1 = priorityQueuePop(heap); var cr = __pattern1.row; var cc = __pattern1.col; var currentKey = key(cr, cc); if(visited.has(currentKey)) { continue; } visited.add(currentKey); visitNode(cr, cc); if(((cr == end[0]) && (cc == end[1]))) { reconstructPath(prev, start, end); return; } foreach(var (nr, nc) in getNeighbors(grid, cr, cc, allowDiag)) { var neighborKey = key(nr, nc); if(visited.has(neighborKey)) { continue; } var nd = (dist[cr][cc] + stepCost(grid, cr, cc, nr, nc)); if((nd < dist[nr][nc])) { dist[nr][nc] = nd; prev.set(neighborKey, [cr, cc]); priorityQueuePush(heap, {priority: nd, row: nr, col: nc}); pushFrontier(nr, nc); } } } reportNoPath(); }
#include <stdio.h> void dijkstraPathfind(int grid, int start, int end, int allowDiag) { var rows = grid.length; var cols = grid[0].length; var dist = Matrix(rows, cols, Infinity); var visited = Set(); var prev = Map(); var heap = PriorityQueue("priority"); dist[start[0]][start[1]] = 0; priorityQueuePush(heap, {priority: 0, row: start[0], col: start[1]}); while(priorityQueueSize(heap)) { var __pattern1 = priorityQueuePop(heap); var cr = __pattern1.row; var cc = __pattern1.col; var currentKey = key(cr, cc); if(visited.has(currentKey)) { continue; } visited.add(currentKey); visitNode(cr, cc); if(((cr == end[0]) && (cc == end[1]))) { reconstructPath(prev, start, end); return; } for(int _fod_i = 0; _fod_i < getNeighbors(grid, cr, cc, allowDiag)_len; _fod_i++) { int nr = getNeighbors(grid, cr, cc, allowDiag)[_fod_i][0]; int nc = getNeighbors(grid, cr, cc, allowDiag)[_fod_i][1]; var neighborKey = key(nr, nc); if(visited.has(neighborKey)) { continue; } var nd = (dist[cr][cc] + stepCost(grid, cr, cc, nr, nc)); if((nd < dist[nr][nc])) { dist[nr][nc] = nd; prev.set(neighborKey, [cr, cc]); priorityQueuePush(heap, {priority: nd, row: nr, col: nc}); pushFrontier(nr, nc); } } } reportNoPath(); }
Advantages
- Finds the optimal lowest-cost path on graphs with non-negative weights.
- Handles weighted terrain that BFS cannot.
Disadvantages
- Expands in all directions without goal awareness, so it visits more nodes than A*.
- Does not support negative edge weights.
When to use it
Use it for weighted shortest paths when you have no admissible heuristic; add a heuristic and it becomes A*.