Number of Islands
Count islands in a grid with DFS or BFS flood fill — graph framing, complexity, and common grid bugs.
- dsa
- graph
- dfs
- bfs
- matrix
- interview
- Meta
- Amazon
- Microsoft
- Uber
The problem
Given an m × n 2D binary grid grid where '1' is land and '0' is water, return the number of islands.
An island is land connected 4-directionally (up/down/left/right) — not diagonals unless the interviewer says so.
Input:
[
["1","1","1","1","0"],
["1","1","0","1","0"],
["1","1","0","0","0"],
["0","0","0","0","0"]
]
Output: 1
Input:
[
["1","1","0","0","0"],
["1","1","0","0","0"],
["0","0","1","0","0"],
["0","0","0","1","1"]
]
Output: 3
Mental model: each cell is a graph node; edges to 4-neighbors that are land. Count connected components.
Brute / naive thoughts
Scanning and “remembering shapes” without a visited structure fails on non-rectangles. You need flood fill or Union-Find.
Optimal: DFS flood fill
Scan every cell. On each unvisited land:
islands++- DFS/BFS mark the whole component as water (or visited)
Mutating the grid to '0' avoids a separate visited matrix (clarify if mutation is OK).
function numIslands(grid: string[][]): number {
if (!grid.length) return 0;
const rows = grid.length;
const cols = grid[0].length;
let count = 0;
function dfs(r: number, c: number): void {
if (r < 0 || c < 0 || r >= rows || c >= cols) return;
if (grid[r][c] !== "1") return;
grid[r][c] = "0";
dfs(r + 1, c);
dfs(r - 1, c);
dfs(r, c + 1);
dfs(r, c - 1);
}
for (let r = 0; r < rows; r++) {
for (let c = 0; c < cols; c++) {
if (grid[r][c] === "1") {
count++;
dfs(r, c);
}
}
}
return count;
}
| Time | O(m · n) — each cell entered O(1) times |
| Space | O(m · n) worst-case recursion depth (one big island) |
BFS version (safer stack depth)
function numIslandsBfs(grid: string[][]): number {
const rows = grid.length;
if (!rows) return 0;
const cols = grid[0].length;
let count = 0;
const dirs = [
[1, 0],
[-1, 0],
[0, 1],
[0, -1],
];
function bfs(sr: number, sc: number): void {
const q: [number, number][] = [[sr, sc]];
grid[sr][sc] = "0";
while (q.length) {
const [r, c] = q.shift()!;
for (const [dr, dc] of dirs) {
const nr = r + dr;
const nc = c + dc;
if (nr < 0 || nc < 0 || nr >= rows || nc >= cols) continue;
if (grid[nr][nc] !== "1") continue;
grid[nr][nc] = "0";
q.push([nr, nc]);
}
}
}
for (let r = 0; r < rows; r++) {
for (let c = 0; c < cols; c++) {
if (grid[r][c] === "1") {
count++;
bfs(r, c);
}
}
}
return count;
}
Interview note: shift() is O(n) on arrays — for huge grids use an index pointer or deque. Fine for interview-scale grids if you mention it.
Union-Find (optional flex)
Union each land with right/down neighbors; count roots that are land. Good signal if you’ve practiced DSU; not required for the classic problem.
Edge cases
- Empty grid / empty rows
- All water → 0
- All land → 1
- Single cell
- Checkerboard lands → many islands of size 1
- Diagonal touch only — not same island under 4-connectivity
Common bugs
- 8-directional accidental diagonals
- Not marking visited before enqueue (BFS) → exponential blowup
- Bounds checks after indexing
- Counting cells instead of components
- Deep DFS on large island → stack overflow in some runtimes; switch to BFS/iterative DFS
Follow-ups
- Max Area of Island — size of largest component
- Pacific Atlantic Water Flow — multi-source DFS
- Number of enclaves / closed islands
- Surrounded regions — flood from borders
Interview delivery
- Clarify 4- vs 8-connectivity and mutability.
- Frame as connected components.
- Implement DFS or BFS flood fill.
- Complexity O(mn).
- Mention stack depth / BFS if they probe.