Off-by-one on range boundaries
Wrong move: Loop endpoints miss first/last candidate.
Usually fails on: Fails on minimal arrays and exact-boundary answers.
Fix: Re-derive loops from inclusive/exclusive ranges before coding.
Move from brute-force thinking to an efficient approach using array strategy.
Given a 2D grid consists of 0s (land) and 1s (water). An island is a maximal 4-directionally connected group of 0s and a closed island is an island totally (all left, top, right, bottom) surrounded by 1s.
Return the number of closed islands.
Example 1:
Input: grid = [[1,1,1,1,1,1,1,0],[1,0,0,0,0,1,1,0],[1,0,1,0,1,1,1,0],[1,0,0,0,0,1,0,1],[1,1,1,1,1,1,1,0]] Output: 2 Explanation: Islands in gray are closed because they are completely surrounded by water (group of 1s).
Example 2:
Input: grid = [[0,0,1,0,0],[0,1,0,1,0],[0,1,1,1,0]] Output: 1
Example 3:
Input: grid = [[1,1,1,1,1,1,1],
[1,0,0,0,0,0,1],
[1,0,1,1,1,0,1],
[1,0,1,0,1,0,1],
[1,0,1,1,1,0,1],
[1,0,0,0,0,0,1],
[1,1,1,1,1,1,1]]
Output: 2
Constraints:
1 <= grid.length, grid[0].length <= 1000 <= grid[i][j] <=1Problem summary: Given a 2D grid consists of 0s (land) and 1s (water). An island is a maximal 4-directionally connected group of 0s and a closed island is an island totally (all left, top, right, bottom) surrounded by 1s. Return the number of closed islands.
Start with the most direct exhaustive search. That gives a correctness anchor before optimizing.
Pattern signal: Array · Union-Find
[[1,1,1,1,1,1,1,0],[1,0,0,0,0,1,1,0],[1,0,1,0,1,1,1,0],[1,0,0,0,0,1,0,1],[1,1,1,1,1,1,1,0]]
[[0,0,1,0,0],[0,1,0,1,0],[0,1,1,1,0]]
[[1,1,1,1,1,1,1],[1,0,0,0,0,0,1],[1,0,1,1,1,0,1],[1,0,1,0,1,0,1],[1,0,1,1,1,0,1],[1,0,0,0,0,0,1],[1,1,1,1,1,1,1]]
Source-backed implementations are provided below for direct study and interview prep.
// Accepted solution for LeetCode #1254: Number of Closed Islands
class Solution {
private int m;
private int n;
private int[][] grid;
public int closedIsland(int[][] grid) {
m = grid.length;
n = grid[0].length;
this.grid = grid;
int ans = 0;
for (int i = 0; i < m; ++i) {
for (int j = 0; j < n; ++j) {
if (grid[i][j] == 0) {
ans += dfs(i, j);
}
}
}
return ans;
}
private int dfs(int i, int j) {
int res = i > 0 && i < m - 1 && j > 0 && j < n - 1 ? 1 : 0;
grid[i][j] = 1;
int[] dirs = {-1, 0, 1, 0, -1};
for (int k = 0; k < 4; ++k) {
int x = i + dirs[k], y = j + dirs[k + 1];
if (x >= 0 && x < m && y >= 0 && y < n && grid[x][y] == 0) {
res &= dfs(x, y);
}
}
return res;
}
}
// Accepted solution for LeetCode #1254: Number of Closed Islands
func closedIsland(grid [][]int) (ans int) {
m, n := len(grid), len(grid[0])
dirs := [5]int{-1, 0, 1, 0, -1}
var dfs func(i, j int) int
dfs = func(i, j int) int {
res := 1
if i == 0 || i == m-1 || j == 0 || j == n-1 {
res = 0
}
grid[i][j] = 1
for k := 0; k < 4; k++ {
x, y := i+dirs[k], j+dirs[k+1]
if x >= 0 && x < m && y >= 0 && y < n && grid[x][y] == 0 {
res &= dfs(x, y)
}
}
return res
}
for i, row := range grid {
for j, v := range row {
if v == 0 {
ans += dfs(i, j)
}
}
}
return
}
# Accepted solution for LeetCode #1254: Number of Closed Islands
class Solution:
def closedIsland(self, grid: List[List[int]]) -> int:
def dfs(i: int, j: int) -> int:
res = int(0 < i < m - 1 and 0 < j < n - 1)
grid[i][j] = 1
for a, b in pairwise(dirs):
x, y = i + a, j + b
if 0 <= x < m and 0 <= y < n and grid[x][y] == 0:
res &= dfs(x, y)
return res
m, n = len(grid), len(grid[0])
dirs = (-1, 0, 1, 0, -1)
return sum(grid[i][j] == 0 and dfs(i, j) for i in range(m) for j in range(n))
// Accepted solution for LeetCode #1254: Number of Closed Islands
struct Solution;
impl Solution {
fn closed_island(mut grid: Vec<Vec<i32>>) -> i32 {
let mut res = 0;
let n = grid.len();
let m = grid[0].len();
for i in 0..n {
for j in 0..m {
if grid[i][j] == 0 && Self::dfs(i, j, &mut grid, n, m) {
res += 1;
}
}
}
res
}
fn dfs(i: usize, j: usize, grid: &mut Vec<Vec<i32>>, n: usize, m: usize) -> bool {
grid[i][j] = 1;
let top = if i > 0 {
if grid[i - 1][j] == 1 {
true
} else {
Self::dfs(i - 1, j, grid, n, m)
}
} else {
false
};
let left = if j > 0 {
if grid[i][j - 1] == 1 {
true
} else {
Self::dfs(i, j - 1, grid, n, m)
}
} else {
false
};
let bottom = if i + 1 < n {
if grid[i + 1][j] == 1 {
true
} else {
Self::dfs(i + 1, j, grid, n, m)
}
} else {
false
};
let right = if j + 1 < m {
if grid[i][j + 1] == 1 {
true
} else {
Self::dfs(i, j + 1, grid, n, m)
}
} else {
false
};
top && left && bottom && right
}
}
#[test]
fn test() {
let grid = vec_vec_i32![
[1, 1, 1, 1, 1, 1, 1, 0],
[1, 0, 0, 0, 0, 1, 1, 0],
[1, 0, 1, 0, 1, 1, 1, 0],
[1, 0, 0, 0, 0, 1, 0, 1],
[1, 1, 1, 1, 1, 1, 1, 0]
];
let res = 2;
assert_eq!(Solution::closed_island(grid), res);
let grid = vec_vec_i32![[0, 0, 1, 0, 0], [0, 1, 0, 1, 0], [0, 1, 1, 1, 0]];
let res = 1;
assert_eq!(Solution::closed_island(grid), res);
}
// Accepted solution for LeetCode #1254: Number of Closed Islands
function closedIsland(grid: number[][]): number {
const m = grid.length;
const n = grid[0].length;
const dirs = [-1, 0, 1, 0, -1];
const dfs = (i: number, j: number): number => {
let res = i > 0 && j > 0 && i < m - 1 && j < n - 1 ? 1 : 0;
grid[i][j] = 1;
for (let k = 0; k < 4; ++k) {
const [x, y] = [i + dirs[k], j + dirs[k + 1]];
if (x >= 0 && y >= 0 && x < m && y < n && grid[x][y] === 0) {
res &= dfs(x, y);
}
}
return res;
};
let ans = 0;
for (let i = 0; i < m; ++i) {
for (let j = 0; j < n; j++) {
if (grid[i][j] === 0) {
ans += dfs(i, j);
}
}
}
return ans;
}
Use this to step through a reusable interview workflow for this problem.
Track components with a list or adjacency matrix. Each union operation may need to update all n elements’ component labels, giving O(n) per union. For n union operations total: O(n²). Find is O(1) with direct lookup, but union dominates.
With path compression and union by rank, each find/union operation takes O(α(n)) amortized time, where α is the inverse Ackermann function — effectively constant. Space is O(n) for the parent and rank arrays. For m operations on n elements: O(m × α(n)) total.
Review these before coding to avoid predictable interview regressions.
Wrong move: Loop endpoints miss first/last candidate.
Usually fails on: Fails on minimal arrays and exact-boundary answers.
Fix: Re-derive loops from inclusive/exclusive ranges before coding.