As Far from Land as Possible
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
grid = [[1,0,1],[0,0,0],[1,0,1]]2Starting 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
| 1 | class 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
Collect All Land Cells as BFS Starting Points
| 3 | n = len(grid) |
| 4 | q = deque() |
| 6 | for r in range(n): |
| 7 | for c in range(n): |
| 8 | if grid[r][c] == 1: |
| 9 | q.append((r, c)) |
| 11 | if 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.
Expand BFS Frontier Layer by Layer to Measure Distance
| 14 | directions = [(1,0), (-1,0), (0,1), (0,-1)] |
| 15 | dist = -1 |
| 17 | while 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)) |
| 30 | return 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.
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.
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.
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.
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.
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.
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.
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.
if grid[r][c] == 1:
Check if the current cell is land.
Identifying land cells is essential to initialize BFS from all sources simultaneously.
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.
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.
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.
r, c = q.popleft()
Dequeue the next cell to process.
FIFO order guarantees BFS explores cells in order of increasing distance from land.
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.
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.
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.
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.
nr, nc = r + dr, c + dc
Calculate neighbor cell coordinates.
Computing neighbor positions is necessary to explore adjacent cells during BFS.
for r in range(n):
Iterate over each row index.
This loop scans the entire grid to identify land cells as BFS starting points.
for c in range(n):
Iterate over each column index.
Nested loop ensures every cell in the grid is visited for initialization.
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