LeetCode #1926 Medium

Nearest Exit from Entrance in Maze

Nearest Exit from Entrance in Maze: find the number of steps to the nearest exit — any empty border cell other than the entrance — or -1 if none is reachable.

Constraints
  • maze.length == m, maze[i].length == n
  • 1 <= m, n <= 100
  • maze[i][j] is either '.' or '+'.
  • entrance.length == 2
  • The entrance cell is empty.
arraybreadth-first-searchmatrix
Open on LeetCode ↗
02

Intuition

Every move costs the same, so the shortest route is found by breadth-first search: expand the frontier one ring at a time and the first exit you touch is the nearest. Depth-first would find an exit but not necessarily the closest.

How to spot this pattern

Unweighted shortest path on a grid is always BFS — the tell is 'nearest', 'fewest steps', or 'minimum moves' with uniform cost. Marking cells as visited at enqueue time rather than dequeue time is the standard efficiency detail. Same shape as Rotting Oranges and Shortest Path in Binary Matrix.

03

Approach

Try it first

Before reading on: since every step costs the same, which traversal guarantees the first exit you reach is the closest? Then state exactly what makes a cell an exit. Aim for O(m·n).

1

Why BFS and not DFS

All moves cost 1, which makes this an unweighted shortest-path problem. BFS explores in order of distance, so every cell is first reached by the shortest possible route and the first exit encountered is the nearest — you can return immediately. DFS plunges down one corridor to the end and would have to explore the entire maze and compare, doing far more work for the same answer.

2

Tracking distance in the queue

Push (row, col, steps) and increment steps when enqueueing neighbours. The alternative is a level-by-level loop that drains the queue in batches and counts rounds — both are correct, and carrying the count per cell is slightly simpler here since it needs no batch bookkeeping. Mark cells as walls ('+') the moment they are enqueued, not when they are dequeued, so the same cell is never queued twice.

3

Defining an exit precisely

An exit is an empty cell on the border that is not the entrance. Two checks are needed: the cell lies in row 0, the last row, column 0, or the last column; and it differs from the entrance coordinates. Forgetting the second condition makes the entrance itself count as an exit and returns 0 for every input. Note the entrance is never an exit even though it is often on the border. Cost is O(m·n) — each cell is enqueued at most once.

04

Solution & live demo

1class Solution:
2 def nearestExit(self, maze, entrance):
3 rows, cols = len(maze), len(maze[0])
4 start_row, start_col = entrance
5 queue = deque([(start_row, start_col, 0)])
6 maze[start_row][start_col] = "+"
7 while queue:
8 row, col, steps = queue.popleft()
9 for dr, dc in ((1, 0), (-1, 0), (0, 1), (0, -1)):
10 r, c = row + dr, col + dc
11 if not (0 <= r < rows and 0 <= c < cols):
12 continue
13 if maze[r][c] == "+":
14 continue
15 if r in (0, rows - 1) or c in (0, cols - 1):
16 return steps + 1
17 maze[r][c] = "+"
18 queue.append((r, c, steps + 1))
19 return -1
05

Common pitfalls

Counting the entrance as an exit

✗ Wrong
if r in (0, rows-1) or c in (0, cols-1):
    return steps
✓ Right
# only neighbours are tested, never the entrance itself

The entrance is excluded by definition. Testing the starting cell for border-ness returns 0 immediately whenever the entrance sits on the edge, which it usually does.

Using DFS

✗ Wrong
stack.pop()  # depth-first
✓ Right
queue.popleft()  # breadth-first

DFS finds an exit but not the nearest, since it commits to one corridor before trying alternatives. Only BFS guarantees the first exit reached is the closest.

Marking visited on dequeue

✗ Wrong
row, col, steps = queue.popleft()
maze[row][col] = '+'
✓ Right
maze[r][c] = '+'
queue.append((r, c, steps + 1))

Between being enqueued and dequeued, a cell can be enqueued again by another neighbour, so the queue bloats with duplicates and the work multiplies. Mark at push time.

06

Edge cases

Entrance is on the border

It is explicitly excluded, so the search must still travel to a different border cell.

No reachable exit

The queue drains without finding one and the answer is -1.

Exit adjacent to the entrance

Found on the first expansion, giving 1 step.

Entirely walled in

No neighbours can be enqueued, so the loop ends immediately with -1.

Single row or column maze

The border test covers every cell; movement is limited to one axis.

07

Complexity

Time
O(m · n)
Space
O(m · n)
Each cell is enqueued at most once; the queue can hold a full frontier.