As Far from Land as Possible

Medium BFS

Problem

Given an n x n grid containing 0s (water) and 1s (land), return the maximum distance from any water cell to the nearest land cell. If no water or no land exists, return -1.

  • n == grid.length
  • n == grid[i].length
  • 1 ≤ n ≤ 100
  • grid[i][j] is 0 or 1

Example

Input: grid = [[1,0,1],[0,0,0],[1,0,1]]
Output: 2

Starting from all land cells simultaneously, the BFS expands layer by layer into water cells. The farthest water cell from any land is at distance 2. The algorithm initializes the queue with all land cells, then explores neighbors in waves. The critical moment is when the BFS reaches the last layer of water cells, which determines the maximum distance.

Approach

Straightforward Solution

A naive approach would run BFS from each water cell to find the nearest land, resulting in O(n^4) time complexity, which is infeasible for large grids.

Core Observation

The shortest distance from any water cell to land can be found by simultaneously expanding from all land cells using BFS. This multi-source BFS treats all land cells as starting points and explores outward in layers, ensuring the first time a water cell is reached is via the shortest path.

Path to Optimal

Recognizing that BFS from each water cell is inefficient, the key insight is to reverse the problem: start BFS from all land cells simultaneously. This multi-source BFS expands outward, marking distances to water cells as layers increase. This approach guarantees O(n^2) time since each cell is visited at most once.

Optimal Approach

Initialize a queue with all land cells. Perform BFS, expanding to adjacent water cells, marking them as visited by converting them to land. Track the number of BFS layers expanded; the last layer count is the maximum distance. Return -1 if the grid is all land or all water.

Time

O(n^2)

Each cell is enqueued and dequeued at most once during BFS, and each neighbor check is constant time, resulting in O(n^2) total operations for an n x n grid.

Space

O(n^2)

The queue can hold up to O(n^2) cells in the worst case, and the grid is modified in-place to mark visited cells, so auxiliary space is O(n^2) due to the queue.

Pattern Spotlight

BFS (Multi-Source Traversal)

When shortest distances from multiple sources are needed simultaneously, enqueue all sources at once and perform BFS layer by layer, ensuring each node's first visit is via the shortest path from any source.

Solution

1class Solution:
2 def maxDistance(self, grid: List[List[int]]) -> int:
3 n = len(grid)
4 q = deque()
5
6 for r in range(n):
7 for c in range(n):
8 if grid[r][c] == 1:
9 q.append((r, c))
10
11 if not q or len(q) == n * n:
12 return -1
13
14 directions = [(1,0), (-1,0), (0,1), (0,-1)]
15 dist = -1
16
17 while q:
18 dist += 1
19
20 for _ in range(len(q)):
21 r, c = q.popleft()
22
23 for dr, dc in directions:
24 nr, nc = r + dr, c + dc
25
26 if 0 <= nr < n and 0 <= nc < n and grid[nr][nc] == 0:
27 grid[nr][nc] = 1
28 q.append((nr, nc))
29
30 return dist

Step-by-Step Solution

1

Collect All Land Cells as BFS Starting Points

3n = len(grid)
4q = deque()
6for r in range(n):
7 for c in range(n):
8 if grid[r][c] == 1:
9 q.append((r, c))
11if not q or len(q) == n * n:
12 return -1

Objective

To identify all land cells and enqueue them as the initial BFS frontier.

Key Insight

By treating all land cells as sources, the BFS can expand outward simultaneously, efficiently computing the shortest distance to water cells. This multi-source initialization avoids repeated BFS from each water cell, drastically reducing time complexity.

Interview Quick-Check

Core Logic

Enqueueing all land cells at once sets up a multi-source BFS that finds shortest distances to water cells in a single pass.

State & Boundaries

Check if the queue is empty or full (all land) to handle edge cases where no valid distance exists.

Common Pitfalls & Bugs

Forgetting to enqueue all land cells initially leads to incorrect distance calculations or incomplete BFS.

2

Expand BFS Frontier Layer by Layer to Measure Distance

14directions = [(1,0), (-1,0), (0,1), (0,-1)]
15dist = -1
17while q:
18 dist += 1
20 for _ in range(len(q)):
21 r, c = q.popleft()
23 for dr, dc in directions:
24 nr, nc = r + dr, c + dc
26 if 0 <= nr < n and 0 <= nc < n and grid[nr][nc] == 0:
27 grid[nr][nc] = 1
28 q.append((nr, nc))
30return dist

Objective

To perform BFS expansion from land cells into water cells, tracking the distance in layers.

Key Insight

Each BFS iteration represents one step outward from land. By incrementing the distance counter at each layer and marking visited water cells as land, the algorithm ensures that the distance recorded corresponds to the shortest path from any land cell. This layer-by-layer expansion naturally computes the maximum distance to the farthest water cell.

Interview Quick-Check

Core Logic

Incrementing distance at each BFS layer and marking visited water cells prevents revisiting and ensures shortest path discovery.

State & Boundaries

The BFS loop continues until no more water cells can be reached, at which point the distance variable holds the maximum distance.

Common Pitfalls & Bugs

Failing to mark visited water cells immediately upon enqueueing can cause infinite loops or incorrect distances.

Line Analysis

This solution has 3 Critical lines interviewers watch for.

Line 9 Critical
q.append((r, c))

Add land cell coordinates to the BFS queue.

This line is critical because it seeds the BFS with all land cells, ensuring the search expands simultaneously from all sources and finds shortest distances efficiently.

Line 18 Critical
dist += 1

Increment distance at the start of each BFS layer.

Incrementing distance at the start of each BFS layer is the core mechanism that measures how far the BFS has expanded from land, directly yielding the maximum distance to water.

Line 26 Critical
if 0 <= nr < n and 0 <= nc < n and grid[nr][nc] == 0:

Check if neighbor is within bounds and is water.

This compound condition is critical to BFS correctness, ensuring only valid, unvisited water cells are enqueued, preventing infinite loops and incorrect distance calculations.

Line 4 Core
q = deque()

Initialize a queue for BFS traversal.

The queue holds cells to explore in BFS order, enabling layer-by-layer expansion from multiple sources.

Line 11 Core
if not q or len(q) == n * n:

Check if the queue is empty or full (all land).

If no land or no water exists, the problem has no valid solution, so returning -1 early avoids unnecessary computation.

Line 27 Core
grid[nr][nc] = 1

Mark the water cell as visited by converting it to land.

Marking visited cells prevents revisiting and ensures BFS progresses correctly without cycles.

Line 30 Core
return dist

Return the maximum distance found after BFS completes.

After BFS finishes, the distance variable holds the maximum shortest distance from any water cell to land, which is the problem's answer.

Line 8 Core
if grid[r][c] == 1:

Check if the current cell is land.

Identifying land cells is essential to initialize BFS from all sources simultaneously.

Line 12 Core
return -1

Return -1 when no valid distance can be computed.

This early return handles edge cases where the grid is all land or all water, as no water cell exists to measure distance from land or vice versa.

Line 15 Core
dist = -1

Initialize distance counter to -1.

Starting at -1 ensures that after the first BFS layer increment, distance corresponds to the correct number of steps from land to water.

Line 17 Core
while q:

Continue BFS while there are cells to explore.

The loop processes all reachable water cells layer by layer until no new water cells remain.

Line 21 Core
r, c = q.popleft()

Dequeue the next cell to process.

FIFO order guarantees BFS explores cells in order of increasing distance from land.

Line 28 Core
q.append((nr, nc))

Enqueue the newly discovered water cell for BFS expansion.

Adding neighbors to the queue schedules them for processing in the next BFS layer, enabling distance propagation.

Line 14 Standard
directions = [(1,0), (-1,0), (0,1), (0,-1)]

Define the four possible movement directions.

These directions represent the allowed moves (up, down, left, right) for BFS traversal, enabling exploration of adjacent cells.

Line 20 Standard
for _ in range(len(q)):

Iterate over all cells in the current BFS layer.

Processing all cells in the current layer ensures BFS expands uniformly, maintaining correct distance calculations.

Line 23 Standard
for dr, dc in directions:

Iterate over all four possible directions.

Exploring neighbors in all directions ensures BFS covers the entire reachable area from land cells.

Line 24 Standard
nr, nc = r + dr, c + dc

Calculate neighbor cell coordinates.

Computing neighbor positions is necessary to explore adjacent cells during BFS.

Line 6 Standard
for r in range(n):

Iterate over each row index.

This loop scans the entire grid to identify land cells as BFS starting points.

Line 7 Standard
for c in range(n):

Iterate over each column index.

Nested loop ensures every cell in the grid is visited for initialization.

Line 3 Basic
n = len(grid)

Store the grid size n.

Caching the grid dimension simplifies boundary checks and avoids repeated calls to len(grid), improving readability and minor performance.

Test Your Understanding

Why does starting BFS from all land cells simultaneously guarantee the shortest distance to water cells?

Because BFS explores neighbors in order of increasing distance, the first time a water cell is reached from any land cell is via the shortest path, and starting from all land cells simultaneously ensures the minimal distance is found for every water cell.

Related Problems

BFS pattern

Don't just read it. Drill it.

Reconstruct As Far from Land as Possible from memory until it sticks. AlgoDrill blanks out key lines and makes you fill them back in, step by step.

Unlock the As Far from Land as Possible drill

or drill a free problem