You are given an m x n binary matrix grid, where 0 represents a sea cell and 1 represents a land cell.
A move consists of walking from one land cell to another adjacent (4-directionally) land cell or walking off the boundary of the grid.
Return the number of land cells in grid for which we cannot walk off the boundary of the grid in any number of moves.
Example 1:
Input: grid = [
[0,1,0,0],
[1,1,0,0],
[0,0,1,0],
[0,0,0,1]
]
Output: 1 Example 2:
Input: grid = [
[1,0,0,0],
[0,1,1,0],
[0,1,0,1],
[1,0,0,1]
]
Output: 3 Constraints:
1 <= grid.length, grid[i].length <= 500grid[i][j] is either 0 or 1.Before attempting this problem, you should be comfortable with:
An enclave is a land cell that cannot reach the grid boundary by walking only on land. Any land cell connected to the boundary can eventually "walk off" the grid, so it is not an enclave. Our strategy is to count total land cells, then subtract those connected to the boundary. We find boundary-connected land by starting dfs from every land cell on the boundary and counting all reachable land cells.
dfs to count all connected land cells.dfs marks cells as visited and returns the count of land cells in that component.class Solution:
def numEnclaves(self, grid: List[List[int]]) -> int:
ROWS, COLS = len(grid), len(grid[0])
direct = [[0, 1], [0, -1], [1, 0], [-1, 0]]
# Return num of land cells
def dfs(r, c):
if (r < 0 or c < 0 or
r == ROWS or c == COLS or
not grid[r][c] or (r, c) in visit):
return 0
visit.add((r, c))
res = 1
for dr, dc in direct:
res += dfs(r + dr, c + dc)
return res
visit = set()
land, borderLand = 0, 0
for r in range(ROWS):
for c in range(COLS):
land += grid[r][c]
if (grid[r][c] and (r, c) not in visit and
(c in [0, COLS - 1] or r in [0, ROWS - 1])):
borderLand += dfs(r, c)
return land - borderLandWhere is the number of rows and is the number of columns in the given grid.
bfs offers an iterative approach to the same problem. Instead of recursive dfs, we use a queue to explore boundary-connected land cells. We first add all boundary land cells to the queue, then process them level by level, marking visited cells. After bfs completes, we have counted all land that can reach the boundary. The remaining land cells are enclaves.
class Solution:
def numEnclaves(self, grid: list[list[int]]) -> int:
ROWS, COLS = len(grid), len(grid[0])
direct = [[0, 1], [0, -1], [1, 0], [-1, 0]]
visit = [[False] * COLS for _ in range(ROWS)]
q = deque()
land, borderLand = 0, 0
for r in range(ROWS):
for c in range(COLS):
land += grid[r][c]
if (grid[r][c] == 1 and
(r in [0, ROWS - 1] or c in [0, COLS - 1])
):
q.append((r, c))
visit[r][c] = True
while q:
r, c = q.popleft()
borderLand += 1
for dr, dc in direct:
nr, nc = r + dr, c + dc
if (0 <= nr < ROWS and 0 <= nc < COLS and
grid[nr][nc] == 1 and not visit[nr][nc]
):
q.append((nr, nc))
visit[nr][nc] = True
return land - borderLandWhere is the number of rows and is the number of columns in the given grid.
Union-Find provides another perspective on this problem. We create a virtual boundary node and union all boundary land cells with it. We also union adjacent land cells together. After processing, the size of the boundary node's component tells us how many land cells can reach the boundary. The answer is total land minus this count (plus 1 to account for the virtual node itself being counted in the size).
ROWS * COLS + 1, where index N represents the boundary.N.find(N).land - borderLand + 1 (the +1 adjusts for the virtual boundary node).class DSU:
def __init__(self, n):
self.Parent = list(range(n + 1))
self.Size = [1] * (n + 1)
def find(self, node):
if self.Parent[node] != node:
self.Parent[node] = self.find(self.Parent[node])
return self.Parent[node]
def union(self, u, v):
pu, pv = self.find(u), self.find(v)
if pu == pv:
return False
if self.Size[pu] >= self.Size[pv]:
self.Size[pu] += self.Size[pv]
self.Parent[pv] = pu
else:
self.Size[pv] += self.Size[pu]
self.Parent[pu] = pv
return True
class Solution:
def numEnclaves(self, grid: list[list[int]]) -> int:
ROWS, COLS = len(grid), len(grid[0])
N = ROWS * COLS
def index(r, c):
return r * COLS + c
dsu = DSU(N)
directions = [0, 1, 0, -1, 0]
land = 0
for r in range(ROWS):
for c in range(COLS):
if grid[r][c] == 0:
continue
land += 1
for d in range(4):
nr, nc = r + directions[d], c + directions[d + 1]
if 0 <= nr < ROWS and 0 <= nc < COLS:
if grid[nr][nc] == 1:
dsu.union(index(r, c), index(nr, nc))
else:
dsu.union(N, index(r, c))
borderLand = dsu.Size[dsu.find(N)]
return land - borderLand + 1Where is the number of rows and is the number of columns in the given grid.
A common mistake is to count all land cells that are not on the boundary. However, an enclave is defined as land that cannot reach any boundary cell through adjacent land connections. Interior land cells connected to boundary land are not enclaves and must be excluded from the count.
The efficient approach starts traversal from boundary land cells and marks all reachable land. Starting from each interior cell and checking if it can reach the boundary is much slower and prone to errors. Always initiate searches from boundary cells to find non-enclave land.
When checking if a cell is on the boundary, ensure you correctly identify cells where row equals 0 or ROWS - 1, and column equals 0 or COLS - 1. Using incorrect boundary conditions (like row == ROWS instead of row == ROWS - 1) will cause cells to be misclassified as boundary or interior cells.