LeetCode #1020 Medium

Number of Enclaves

Number of Enclaves: in a binary grid where 1 is land, you may walk between adjacent land cells and step off the grid from a border cell. Return the number of land cells from which you can never walk off.

Constraints
  • m == grid.length
  • n == grid[i].length
  • 1 <= m, n <= 500
  • grid[i][j] is either 0 or 1.
graphsdfsbfsmatrix
Open on LeetCode ↗
02

Intuition

Number of enclaves counts land cells from which it is impossible to walk off the grid, moving only through adjacent land. Land touching the border, or connected to it, can always escape. Searching outward from each land cell to test whether it escapes would repeat enormous amounts of work. The efficient method inverts the question: - Instead of finding trapped land, find the land that can escape — flood-fill inward from every border land cell, and whatever remains is enclosed. Every cell reachable from the border escapes by definition, so one traversal per border cell marks all of them, and a final count of unmarked land gives the answer directly. So the algorithm has three phases: traverse from all border land cells, marking as you go; then scan the whole grid counting land that was never marked. Seeding must include every border cell, not just the corners. All four edges — the full first and last rows and the full first and last columns — need checking, and missing one edge leaves escapable land counted as trapped. Marking can be done by writing 0 over visited land, which removes the need for a separate visited grid. That mutates the input, which is acceptable here, and makes the final count a simple sum of remaining 1s. Recursive DFS can overflow the stack on a large grid where the land forms one long connected region. BFS or an explicit stack avoids that. Each cell is visited a constant number of times, so the cost is O(m × n) in both time and space.

How to spot this pattern

The same border flood as Surrounded Regions, counting instead of rewriting. Sink every land cell reachable from the edge, then whatever land survives is enclosed. Recognising the shared shape means the second problem costs almost no new thought.

03

Approach

Try it first

Before reading on: price up what the direct approach costs here, then ask what one visit per cell or vertex would have to mark so nothing is counted twice. Aim for O(m x n) time and O(m x n) space.

1

Invert the question

Testing each land cell for escape repeats work endlessly. Find the land that can escape instead — everything left over is trapped.

2

Seed from every border cell

Flood-fill from all land on the full first and last rows and columns, not just the corners. Missing an edge counts escapable land as enclosed.

3

Mark by overwriting

Write 0 over each visited land cell. This removes the need for a separate visited grid and makes the final count a simple sum of remaining 1s.

4

Traverse only through land

Move to adjacent cells only when they hold land. Water blocks movement, so it needs no marking or special handling.

5

Count what survives

Scan the grid and count the land cells still set. These are exactly the cells with no path to the border, which is the answer.

6

Prefer iteration on large grids

Recursive DFS can overflow the stack when land forms one long connected region. BFS or an explicit stack avoids the risk entirely.

7

Cost of the approach

Each cell is visited a constant number of times, giving O(m × n) time and O(m × n) space in the worst case for the traversal structure.

04

Solution & live demo

▶1class Solution:
▶2 def numEnclaves(self, grid):
▶3 R, C = len(grid), len(grid[0])
▶4 stack = []
▶5 for r in range(R):
▶6 for c in range(C):
▶7 if (r in (0, R - 1) or c in (0, C - 1)) and grid[r][c] == 1:
▶8 stack.append((r, c))
▶9 grid[r][c] = 0
▶10 while stack:
▶11 r, c = stack.pop()
▶12 for dr, dc in ((1,0), (-1,0), (0,1), (0,-1)):
▶13 nr, nc = r + dr, c + dc
▶14 if 0 <= nr < R and 0 <= nc < C and grid[nr][nc] == 1:
▶15 grid[nr][nc] = 0
▶16 stack.append((nr, nc))
▶17 return sum(map(sum, grid))
05

Common pitfalls

Counting during the flood

✗ Wrong
count += 1  # inside the flood loop
✓ Right
return sum(map(sum, grid))

The flood visits the cells that escape, which is the opposite of what's being counted. Summing the grid afterwards counts exactly the land that was never reached.

Using a separate visited array instead of sinking

✗ Wrong
visited = [[False] * C for _ in range(R)]
✓ Right
grid[nr][nc] = 0

Not wrong, but it doubles the memory and then requires a second condition in the final count. Overwriting reachable land with water makes the answer a plain sum — and the problem allows mutating the grid.

Not sinking the seed cells themselves

✗ Wrong
stack.append((r, c))
✓ Right
stack.append((r, c))
grid[r][c] = 0

Border land is by definition reachable and must not be counted. Leaving the seeds set also lets the flood re-enter them, pushing duplicates and potentially looping.

06

Edge cases

All land touches the border

The flood sinks everything and the count is 0.

No land at all

No seeds, nothing to count, answer 0.

Single row or column

Every cell is a border cell, so no enclave can exist and the answer is 0.

Multiple separate enclaves

All of their cells are counted together, since the problem asks for a cell count rather than a region count.

07

Complexity

Time
O(m x n)
Space
O(m x n)
Each cell is pushed at most once. Sinking cells in place removes the need for a separate visited grid.