The problem. You're given an n × n grid of 0 (open) and 1 (blocked). Find the length of the shortest path from the top-left cell to the bottom-right cell that only steps on 0s. A step may go to any of the 8 neighbouring cells, diagonals included. The length counts the cells on the path. Return -1 if there is no such path.
grid = [[0,1],
[1,0]] -> 2 (one diagonal step)
grid = [[0,0,0],
[1,1,0],
[1,1,0]] -> 4 ((0,0) -> (0,1) -> (1,2) -> (2,2))
grid = [[1,0,0],
[1,1,0],
[1,1,0]] -> -1 (the start itself is blocked)When every step costs the same, the shortest path is the one with the fewest steps — and BFS finds it for free. BFS visits the start, then everything 1 step away, then everything 2 steps away, and so on, ring by ring. So the first time it reaches a cell, it has come by a shortest route. Record that distance and never touch the cell again.
Any "fewest moves" question fits: moves in a grid, letter changes in a word, turns of a lock, knight jumps. Find the states, find what one move does, and BFS from the start until you meet the target. (When steps cost different amounts, BFS stops working — that's Dijkstra, Pattern 8.)
Walk every simple path from the start with backtracking, and keep the shortest one that reaches the end. Skip a path as soon as it's no shorter than the best found.
const DIRS = [[-1, -1], [-1, 0], [-1, 1], [0, -1], [0, 1], [1, -1], [1, 0], [1, 1]]; function shortestPathBinaryMatrix(grid) { const n = grid.length; let best = Infinity; const onPath = grid.map((row) => row.map(() => false)); const walk = (r, c, length) => { if (r < 0 || r >= n || c < 0 || c >= n || grid[r][c] === 1 || onPath[r][c]) return; if (length >= best) return; // can't beat the best path found so far if (r === n - 1 && c === n - 1) { best = length; return; } onPath[r][c] = true; for (const [dr, dc] of DIRS) walk(r + dr, c + dc, length + 1); onPath[r][c] = false; // free the cell for other paths }; walk(0, 0, 1); return best === Infinity ? -1 : best;}Time exponential, space O(n²). The number of simple paths through an open grid grows explosively; even with the cut-off, a big empty grid is hopeless. DFS dives down one long path first, so it can't know a short one exists until it has wasted time on the long ones.
Give the start distance 1 and put it in a queue. Each cell taken from the queue gives its open, unreached neighbours its distance plus one. The moment the bottom-right cell comes out of the queue, its distance is the answer.
Distances spread ring by ring from the top-left; diagonal steps count. Try the winding grid.
const DIRS = [[-1, -1], [-1, 0], [-1, 1], [0, -1], [0, 1], [1, -1], [1, 0], [1, 1]]; function shortestPathBinaryMatrix(grid) { const n = grid.length; if (grid[0][0] === 1 || grid[n - 1][n - 1] === 1) return -1; // blocked start or end const dist = grid.map((row) => row.map(() => 0)); // 0: not reached yet dist[0][0] = 1; // a path's length counts cells, so the start is 1 const queue = [[0, 0]]; let head = 0; while (head < queue.length) { const [r, c] = queue[head++]; if (r === n - 1 && c === n - 1) return dist[r][c]; // first arrival = shortest for (const [dr, dc] of DIRS) { const nr = r + dr, nc = c + dc; if (nr >= 0 && nr < n && nc >= 0 && nc < n && grid[nr][nc] === 0 && dist[nr][nc] === 0) { dist[nr][nc] = dist[r][c] + 1; // one cell further than the cell that found it queue.push([nr, nc]); } } } return -1; // the queue ran dry: no way through}Time O(n²), space O(n²). Each of the n² cells enters the queue at most once and checks 8 neighbours. The dist grid doubles as the visited marker.
This is the most common mistake in graph interviews: reaching for DFS to find a shortest path. DFS finds a path; BFS finds the shortest one, because it explores in order of distance.
[[0]] is a path of length 1; [[1]] is -1.[[0,1],[1,0]] is 2: with 8 directions, diagonal steps squeeze between blocked cells.