Topics › Graphs › Problems on BFS/DFS
Number of enclaves
LeetCode ↗ Java source ↗ Edit this article ↗
Jump to code:
Problem
You are given an n × m grid where 0 is sea and 1 is land. From a land cell you may step to an adjacent land cell (up, down, left or right), and from a land cell on the border you may also step off the grid. Return the number of land cells from which it is impossible to walk off the grid, no matter how many moves you make. These cells are called enclaves.
Constraints: 1 ≤ n, m ≤ 500.
Example 1
0 0 0 0
1 0 1 0
0 1 1 0
0 0 0 0
answer = 3
(1,0) is on the border, so it can walk off. The three cells (1,2), (2,1), (2,2) form an island that touches no border, so all three are enclaves.
Example 2
0 1 1 0
0 0 1 0
0 0 1 0
0 0 0 0
answer = 0
Every land cell is connected to (0,1) or (0,2) in the top row, so every land cell can escape.
The problem is really about connectivity to the border. A land cell can escape exactly when its island contains at least one border cell. So the answer is the total size of all islands that never touch the border.
Approach 1: Brute force, one search per land cell
Intuition. Follow the statement literally: for every land cell, run a BFS through land and see whether it ever stands on the border. If it does, one more step leaves the grid. Count the cells for which the search fails.
Algorithm.
- For every land cell
(i, j), callcanEscape(i, j). canEscaperuns a BFS over land cells and returnstrueas soon as it dequeues a border cell.- Count the cells for which it returns
false.
static final int[] DR = {-1, 0, 1, 0}, DC = {0, 1, 0, -1};
static int bruteForce(int[][] grid) {
int n = grid.length, m = grid[0].length, count = 0;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (grid[i][j] == 1 && !canEscape(grid, i, j)) count++;
}
}
return count;
}
static boolean canEscape(int[][] grid, int sr, int sc) {
int n = grid.length, m = grid[0].length;
boolean[][] seen = new boolean[n][m];
Deque<int[]> q = new ArrayDeque<>();
q.offer(new int[]{sr, sc});
seen[sr][sc] = true;
while (!q.isEmpty()) {
int[] cell = q.poll();
int r = cell[0], c = cell[1];
if (r == 0 || c == 0 || r == n - 1 || c == m - 1) return true; // one more step walks off the grid
for (int k = 0; k < 4; k++) {
int nr = r + DR[k], nc = c + DC[k];
if (grid[nr][nc] == 1 && !seen[nr][nc]) { seen[nr][nc] = true; q.offer(new int[]{nr, nc}); }
}
}
return false;
}
A cell only expands its neighbours after the border test has failed, so it is an interior cell and its four neighbours are inside the grid. No bounds check is needed.
Complexity. Time O((n·m)^2): an island of k cells is searched k times, and one island can cover most of the grid. Space O(n·m) for one search’s seen array.
Approach 2: Optimal, multi-source BFS from the border
Intuition. Every brute-force search on the same island repeats the same work. Flip the direction: a land cell can escape exactly when it can be reached from a border land cell, because moves between land cells are symmetric. So start one BFS with every border land cell in the queue at once and mark everything it reaches. Those marked cells are the escapable ones. Any land cell left unmarked is an enclave.
Why start them all together instead of one BFS per border cell? Either works and both are linear, since a cell once marked is never expanded again. Putting them all in the queue at the start is simply the cleanest way to say “flood from the entire border”.
Algorithm.
- Enqueue and mark every land cell on the border.
- Run BFS: for each dequeued cell, mark and enqueue every unmarked land neighbour.
- Count land cells that are not marked.
static int optimalBfs(int[][] grid) {
int n = grid.length, m = grid[0].length;
boolean[][] vis = new boolean[n][m];
Deque<int[]> q = new ArrayDeque<>();
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
boolean border = i == 0 || j == 0 || i == n - 1 || j == m - 1;
if (border && grid[i][j] == 1) { vis[i][j] = true; q.offer(new int[]{i, j}); }
}
}
while (!q.isEmpty()) {
int[] cell = q.poll();
for (int k = 0; k < 4; k++) {
int r = cell[0] + DR[k], c = cell[1] + DC[k];
if (r >= 0 && c >= 0 && r < n && c < m && grid[r][c] == 1 && !vis[r][c]) {
vis[r][c] = true;
q.offer(new int[]{r, c});
}
}
}
int count = 0;
for (int i = 0; i < n; i++)
for (int j = 0; j < m; j++)
if (grid[i][j] == 1 && !vis[i][j]) count++;
return count;
}
Dry run on Example 1. Border land cells: only (1,0).
| step | dequeued | newly marked |
|---|---|---|
| start | (1,0) | |
| 1 | (1,0) | none, its neighbours (0,0), (2,0), (1,1) are sea |
The queue is empty. Land cells (1,2), (2,1), (2,2) are unmarked, so the answer is 3.
Complexity. Time O(n·m): every cell is enqueued at most once and has four neighbours. Space O(n·m) for vis and the queue.
Approach 3: DFS per island
Intuition. Another linear method that follows the “answer = total size of islands that never touch the border” view directly. Scan the grid; each time we meet unvisited land, flood its whole island with DFS, counting the cells and noting whether any of them is on the border. If the island never touched the border, add its size to the answer. Each cell is visited once over the whole scan.
The info array is a small trick to return two values from a recursive method: info[0] is the island’s size and info[1] becomes 1 once a border cell is seen.
static int optimalDfs(int[][] grid) {
int n = grid.length, m = grid[0].length, count = 0;
boolean[][] vis = new boolean[n][m];
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (grid[i][j] == 1 && !vis[i][j]) {
int[] info = new int[2]; // {cells in this island, 1 if it touches the border}
explore(grid, i, j, vis, info);
if (info[1] == 0) count += info[0];
}
}
}
return count;
}
static void explore(int[][] grid, int r, int c, boolean[][] vis, int[] info) {
int n = grid.length, m = grid[0].length;
if (r < 0 || c < 0 || r >= n || c >= m || grid[r][c] == 0 || vis[r][c]) return;
vis[r][c] = true;
info[0]++;
if (r == 0 || c == 0 || r == n - 1 || c == m - 1) info[1] = 1;
for (int k = 0; k < 4; k++) explore(grid, r + DR[k], c + DC[k], vis, info);
}
Complexity. Time O(n·m). Space O(n·m) for vis, plus recursion depth up to the size of the largest island, which can reach n·m on a snake-shaped island. For big grids prefer the BFS version.
Edge cases and pitfalls
- Border land is never an enclave. A
1 × 1grid[[1]]gives0, because that cell is on the border. - Do not stop the island flood early. In the DFS version, you might be tempted to return as soon as the border is touched. Then the rest of the island stays unvisited, and the scan would later start a new search from it and count it as a separate, seemingly enclosed island.
- Diagonal neighbours do not connect. Only the four orthogonal directions count.
- Counting cells, not islands. The answer is the number of land cells; an enclosed
3 × 3block contributes9, not1. - Do not destroy the input unless allowed. The methods here use a separate
visarray; a common alternative overwrites reachable land with0, which saves memory but mutates the caller’s grid.
Related problems
- Surrounded Regions uses the same border-first search and flips cells instead of counting them.
- Distance of nearest cell having one is another multi-source BFS on a grid.
- Rotten Oranges spreads from many sources at once, the same queue setup.
- Connected Components Problem in Matrix counts islands with the same flood.
- Pacific Atlantic Water Flow also searches inward from the border.