Find the shortest clear path from the top-left to bottom-right of a 0-1 grid. We explain the intuition, walk through an 8-direction BFS, add comments to every line of code, show a dry run, and give the exact time and space costs.
Here’s the [Problem Link] to begin with.
1. What does the problem ask?
Given an n × n binary matrix grid:
0means an open cell you can walk on.1means a blocked cell you cannot step on.
You start at cell (0,0) and want to reach cell (n-1,n-1).
You may move to any of the 8 neighbouring cells (up, down, left, right, and the four diagonals) as long as the destination cell contains 0.
Return the length of the shortest such path (counting both start and end cells).
If no path exists, return -1.
2. Example
grid = [
[0,1,0],
[1,0,1],
[1,0,0]
]The shortest clear path is:
(0,0) → (1,1) → (2,2)Path length = 3.shortestPathBinaryMatrix(grid) returns 3.
3. Intuition & approach
3.1 Why Breadth-First Search (BFS)?
- All moves have the same cost (1 step).
- BFS explores level by level:
- Level 1: start cell.
- Level 2: all cells one step away.
- Level 3: two steps away, and so on.
- The first time BFS reaches the bottom-right cell, that distance is already the shortest possible.
3.2 Eight directions
At each cell we can move to any of these offsets:
(-1, 0) (-1,-1) (-1, 1)
( 0,-1) ... ( 0, 1)
( 1, 0) ( 1,-1) ( 1, 1)Checking bounds and blocked cells avoids invalid steps.
3.3 Distance grid
We keep a 2-D array distance:
distance[i][j]= shortest path length found so far to reach(i,j).- Initialize all to
∞exceptdistance[0][0] = 1. - Update a neighbour only if the new length is smaller than its stored value.
We can also early-return when the end cell is popped or updated, saving work.
4. Code
import sys
from collections import deque
class Solution:
def shortestPathBinaryMatrix(self, grid: List[List[int]]) -> int:
# Blocked start means no path
if grid[0][0] == 1:
return -1
rows = len(grid)
cols = len(grid[0])
# distance matrix – start cell has distance 1
distance = [[sys.maxsize for _ in range(cols)] for _ in range(rows)]
distance[0][0] = 1
queue = deque()
queue.append([1, 0, 0]) # [current_dist, row, col]
while queue:
dist, i, j = queue.popleft()
# 8 possible moves
for x, y in [
[1, 0], [0, -1], [-1, 0], [0, 1],
[-1, -1], [-1, 1], [1, 1], [1, -1],
]:
new_i, new_j = i + x, j + y
# skip if out of bounds
if new_i < 0 or new_i >= rows or new_j < 0 or new_j >= cols:
continue
# skip if blocked
if grid[new_i][new_j] == 1:
continue
new_dist = dist + 1
# relax distance
if new_dist < distance[new_i][new_j]:
# if we reached the goal, return immediately
if new_i == rows - 1 and new_j == cols - 1:
return new_dist
distance[new_i][new_j] = new_dist
queue.append([new_dist, new_i, new_j])
# If end cell still at ∞, no path exists
return -1 if distance[rows - 1][cols - 1] == sys.maxsize else distance[rows - 1][cols - 1]5. Line-by-line explanation
- Start check – if
(0,0)is blocked, we cannot even step on the grid. distancematrix – carries best path lengths; sentinelsys.maxsizemeans “unreached”.- Queue – each entry holds current known path length plus coordinates.
- BFS loop – pops the next cell in FIFO order (shortest distance first).
- For each neighbour:
- Bounds and block checks.
- Compute
new_dist = dist + 1. - If this is better, store it and push the neighbour.
- Early exit when we first reach
(rows-1, cols-1).
- After BFS, if the bottom-right distance is still
∞, return-1; otherwise return that distance.
6. Dry run on the example grid
| Step | Popped cell | Queue after pushing neighbours | distance change |
|---|---|---|---|
| 1 | (0,0,1) | (1,1) added | distance[1][1] = 2 |
| 2 | (1,1,2) | (2,2) added | distance[2][2] = 3 → goal reached → return 3 |
Path length returned = 3.
7. Complexity
| Measure | Big-O | Details |
|---|---|---|
| Time | O(n²) | Each of the n² cells can enter the queue at most once. |
| Space | O(n²) | distance matrix + queue (worst-case all cells). |
n is the grid size (rows = cols).
8. Conclusion
- BFS is ideal when every move costs the same.
- Track a
distancegrid and relax only when you truly shorten a path. - Because movement is allowed in eight directions, simple Manhattan BFS won’t work—you must include diagonals.
- Early-return as soon as you touch the goal cell to avoid needless work.
With this pattern you can quickly adapt to maze-like problems that ask for the length of the shortest clear path in a binary matrix.
For any changes to the article, kindly email at code@codeanddebug.in or contact us at +91-9712928220.
