You are given an n x n
binary grid, where each cell is either land (1
) or water (0
). Your task is to find the water cell that is as far as possible from any land cell, and return the distance to the nearest land cell for this water cell. The distance between two adjacent cells is 1
(using 4-directional movement: up, down, left, right).
If there is no water cell or no land cell in the grid, return -1
.
0
(water) or 1
(land).The brute-force approach would be, for each water cell, to calculate its distance to every land cell, then pick the minimum. Finally, select the water cell with the maximum of these minimum distances. However, this is very inefficient for larger grids because it requires checking every pair of land and water cells.
Instead, we can optimize this by thinking in terms of "waves" spreading out from all land cells at once. This is a classic use case for Breadth-First Search (BFS): starting from all land cells, we expand outwards, marking the distance to each water cell as we reach it. The last water cell we reach will be the farthest from any land.
This approach is much more efficient and avoids redundant calculations.
We use a multi-source BFS starting from all land cells. Here’s the step-by-step process:
1
).0
), marking their distance as one more than the current cell.-1
as required.This approach ensures each cell is visited at most once, and the BFS guarantees that the first time we reach a water cell, it is through the shortest path from any land cell.
Consider the following grid:
[[1,0,1], [0,0,0], [1,0,1]]
2
as the answer.This demonstrates how BFS "waves" from all land cells simultaneously and finds the farthest water cell efficiently.
W
water and L
land cells, this is O(W * L)
.
O(n^2)
, where n
is the grid size. The space complexity is also O(n^2)
for the queue and visited grid.
The optimized approach is much faster and more scalable for larger grids.
The key insight is to treat all land cells as sources and perform a BFS to find the farthest reachable water cell. This avoids redundant distance calculations and leverages the efficiency of BFS for shortest-path problems. The solution is elegant because it uses a single pass over the grid, ensures optimality, and is easy to implement.
from collections import deque
class Solution:
def maxDistance(self, grid):
n = len(grid)
queue = deque()
# Add all land cells to the queue
for i in range(n):
for j in range(n):
if grid[i][j] == 1:
queue.append((i, j))
# If there are no land or no water cells, return -1
if len(queue) == 0 or len(queue) == n * n:
return -1
dirs = [(-1,0), (1,0), (0,-1), (0,1)]
max_dist = -1
while queue:
x, y = queue.popleft()
for dx, dy in dirs:
nx, ny = x + dx, y + dy
if 0 <= nx < n and 0 <= ny < n and grid[nx][ny] == 0:
grid[nx][ny] = grid[x][y] + 1
max_dist = max(max_dist, grid[nx][ny] - 1)
queue.append((nx, ny))
return max_dist
#include <vector>
#include <queue>
using namespace std;
class Solution {
public:
int maxDistance(vector<vector<int>>& grid) {
int n = grid.size();
queue<pair<int, int>> q;
// Add all land cells to the queue
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
if (grid[i][j] == 1) {
q.push({i, j});
}
}
}
// If there are no land or no water cells, return -1
if (q.empty() || q.size() == n * n) return -1;
vector<pair<int, int>> dirs = {{-1,0}, {1,0}, {0,-1}, {0,1}};
int max_dist = -1;
while (!q.empty()) {
auto [x, y] = q.front(); q.pop();
for (auto [dx, dy] : dirs) {
int nx = x + dx, ny = y + dy;
if (nx >= 0 && nx < n && ny >= 0 && ny < n && grid[nx][ny] == 0) {
grid[nx][ny] = grid[x][y] + 1;
max_dist = max(max_dist, grid[nx][ny] - 1);
q.push({nx, ny});
}
}
}
return max_dist;
}
};
import java.util.*;
class Solution {
public int maxDistance(int[][] grid) {
int n = grid.length;
Queue<int[]> queue = new LinkedList<>();
// Add all land cells to the queue
for (int i = 0; i < n; ++i)
for (int j = 0; j < n; ++j)
if (grid[i][j] == 1)
queue.offer(new int[]{i, j});
// If there are no land or no water cells, return -1
if (queue.isEmpty() || queue.size() == n * n)
return -1;
int[][] dirs = {{-1,0}, {1,0}, {0,-1}, {0,1}};
int maxDist = -1;
while (!queue.isEmpty()) {
int[] cell = queue.poll();
int x = cell[0], y = cell[1];
for (int[] dir : dirs) {
int nx = x + dir[0], ny = y + dir[1];
if (nx >= 0 && nx < n && ny >= 0 && ny < n && grid[nx][ny] == 0) {
grid[nx][ny] = grid[x][y] + 1;
maxDist = Math.max(maxDist, grid[nx][ny] - 1);
queue.offer(new int[]{nx, ny});
}
}
}
return maxDist;
}
}
/**
* @param {number[][]} grid
* @return {number}
*/
var maxDistance = function(grid) {
const n = grid.length;
const queue = [];
// Add all land cells to the queue
for (let i = 0; i < n; ++i) {
for (let j = 0; j < n; ++j) {
if (grid[i][j] === 1) {
queue.push([i, j]);
}
}
}
// If there are no land or no water cells, return -1
if (queue.length === 0 || queue.length === n * n) return -1;
const dirs = [[-1,0], [1,0], [0,-1], [0,1]];
let maxDist = -1;
let idx = 0;
while (idx < queue.length) {
const [x, y] = queue[idx++];
for (const [dx, dy] of dirs) {
const nx = x + dx, ny = y + dy;
if (nx >= 0 && nx < n && ny >= 0 && ny < n && grid[nx][ny] === 0) {
grid[nx][ny] = grid[x][y] + 1;
maxDist = Math.max(maxDist, grid[nx][ny] - 1);
queue.push([nx, ny]);
}
}
}
return maxDist;
};