1020. Number of Enclaves - Explanation

Problem Link

Description

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 <= 500
  • grid[i][j] is either 0 or 1.


Topics

Company Tags

Please upgrade to NeetCode Pro to view company tags.



Prerequisites

Before attempting this problem, you should be comfortable with:

  • 2D Grid Traversal - Understanding how to navigate through a matrix using row/column indices and direction vectors
  • Depth First Search (DFS) - Recursive exploration of connected components in a graph or grid
  • Breadth First Search (BFS) - Iterative level-by-level exploration using a queue
  • Union-Find (Disjoint Set Union) - Data structure for efficiently tracking and merging connected components with path compression and union by rank

Intuition

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.

Algorithm

  1. Iterate through the grid, counting total land cells.
  2. For each land cell on the boundary (first/last row or first/last column), run dfs to count all connected land cells.
  3. The dfs marks cells as visited and returns the count of land cells in that component.
  4. Subtract the boundary-connected land count from total land to get 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]]

        # 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 - borderLand

Time & Space Complexity

  • Time complexity: O(mn)O(m * n)
  • Space complexity: O(mn)O(m * n)

Where mm is the number of rows and nn is the number of columns in the given grid.


Intuition

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.

Algorithm

  1. Count total land cells while iterating through the grid.
  2. Add all boundary land cells to a queue and mark them visited.
  3. Process the queue: for each cell, increment boundary land count and add unvisited land neighbors to the queue.
  4. Return total land minus boundary-connected land.
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 - borderLand

Time & Space Complexity

  • Time complexity: O(mn)O(m * n)
  • Space complexity: O(mn)O(m * n)

Where mm is the number of rows and nn is the number of columns in the given grid.


3. Disjoint Set Union

Intuition

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).

Algorithm

  1. Create a DSU with size ROWS * COLS + 1, where index N represents the boundary.
  2. For each land cell:
    • Count it toward total land.
    • Union with adjacent land cells.
    • If on the boundary (neighbor out of bounds), union with the virtual boundary node N.
  3. Get the size of the boundary component using find(N).
  4. Return 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 + 1

Time & Space Complexity

  • Time complexity: O(mn)O(m * n)
  • Space complexity: O(mn)O(m * n)

Where mm is the number of rows and nn is the number of columns in the given grid.


Common Pitfalls

Counting All Land Cells Instead of Enclaves Only

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.

Starting DFS/BFS From Interior Cells Instead of Boundary

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.

Off-by-One Errors in Boundary Detection

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.