Count the islands in a grid
A grid is given as a list of equal-length strings of '1' (land) and '0'
(water). Two land cells belong to the same island if they share an edge: up, down, left or right, not
diagonally. Return the number of islands.
Examples
| Input | Output | Why |
|---|---|---|
grid = ["11000", "11000", "00100", "00011"] | 3 | A 2 × 2 block, a single cell, and a pair. |
grid = ["101", "010", "101"] | 5 | Diagonal neighbours do not connect: five islands of one cell. |
grid = ["000"] | 0 | No land at all. |
Constraints
- 1 ≤ rows, cols ≤ 300
- Every row has the same length
- Cells are '0' or '1'
Hints
Hint 1
Treat each land cell as a node with up to four neighbours. An island is a connected component.
Hint 2
Scan the grid. When you meet land you have not seen, you have found a new island: count it.
Hint 3
Then visit every cell of that island (depth-first or breadth-first) and mark it seen, so the scan never counts it again.
Where people lose marks
- Treating diagonal cells as neighbours. "5" in the second example becomes "1".
- Recursive flood fill on a large grid: Python stops at depth 1,000 and other languages overflow the stack.
- Marking a cell when it is popped rather than when it is pushed, so the same cell is pushed several times and the search slows down badly.
- Modifying the input grid to mark cells, when the interviewer asked you not to. Keep a separate seen table, or ask first.
Dry run on ["11000","11000","00100","00011"]
Read it one frame at a time, the way you would trace the code on paper in an interview. Each frame shows the state after the step it describes.
-
Step 1 / 4row 0A0A1·2·3·4row 1A0A1·2·3·4row 2·0·112·3·4row 3·0·1·21314islands1
Unseen land at (0, 0): island A. Flooding it marks 4 cells, so the scan will skip them.
-
Step 2 / 4row 0A0A1·2·3·4row 1A0A1·2·3·4row 2·0·1B2·3·4row 3·0·1·21314islands2
Unseen land at (2, 2): island B. Flooding it marks 1 cell, so the scan will skip it.
-
Step 3 / 4row 0A0A1·2·3·4row 1A0A1·2·3·4row 2·0·1B2·3·4row 3·0·1·2C3C4islands3
Unseen land at (3, 3): island C. Flooding it marks 2 cells, so the scan will skip them.
-
Step 4 / 4row 0A0A1·2·3·4row 1A0A1·2·3·4row 2·0·1B2·3·4row 3·0·1·2C3C4answer3
The scan reaches the end. Every land cell belongs to a lettered island: 3 in all.
How to think about it
The grid is a graph whose nodes are land cells and whose edges join cells that share a side. The number of islands is the number of connected components, and the standard way to count components is to scan every node and, each time you reach one that has not been visited, start a search from it that marks its whole component.
- Scan cells in order. An unvisited land cell is the first cell of a new island: add one to the count.
- Flood from it with an explicit stack, marking each land neighbour as seen when it is pushed, so no cell enters the stack twice.
- When the stack empties, the island is fully marked and the scan continues.
An explicit stack rather than recursion matters here: a 300 × 300 island is 90,000 cells deep, far beyond Python's default recursion limit of 1,000.
Complexity. Time O(rows · cols) · Space O(rows · cols)
The solution, in six languages
Each one is compiled and run against the test table before it is published. Java is reviewed by hand.
def num_islands(grid):
rows, cols = len(grid), len(grid[0])
seen = [[False] * cols for _ in range(rows)]
count = 0
for r in range(rows):
for c in range(cols):
if grid[r][c] != '1' or seen[r][c]:
continue
count += 1 # a new island: flood it so it is never counted again
seen[r][c] = True
stack = [(r, c)]
while stack:
i, j = stack.pop()
for a, b in ((i + 1, j), (i - 1, j), (i, j + 1), (i, j - 1)):
if 0 <= a < rows and 0 <= b < cols and grid[a][b] == '1' and not seen[a][b]:
seen[a][b] = True # mark on push: no cell enters the stack twice
stack.append((a, b))
return count#include <string>
#include <utility>
#include <vector>
int solve(const std::vector<std::string>& grid) {
const int rows = grid.size(), cols = grid[0].size();
std::vector<std::vector<bool>> seen(rows, std::vector<bool>(cols, false));
const int dr[] = {1, -1, 0, 0}, dc[] = {0, 0, 1, -1};
int count = 0;
for (int r = 0; r < rows; ++r) {
for (int c = 0; c < cols; ++c) {
if (grid[r][c] != '1' || seen[r][c]) continue;
++count; // a new island: flood it
seen[r][c] = true;
std::vector<std::pair<int, int>> stack{{r, c}};
while (!stack.empty()) {
auto [i, j] = stack.back();
stack.pop_back();
for (int k = 0; k < 4; ++k) {
const int a = i + dr[k], b = j + dc[k];
if (a >= 0 && a < rows && b >= 0 && b < cols && grid[a][b] == '1' && !seen[a][b]) {
seen[a][b] = true; // mark on push
stack.push_back({a, b});
}
}
}
}
}
return count;
}class Solution {
public int numIslands(String[] grid) {
int rows = grid.length, cols = grid[0].length();
boolean[][] seen = new boolean[rows][cols];
int[] dr = {1, -1, 0, 0}, dc = {0, 0, 1, -1};
java.util.ArrayDeque<int[]> stack = new java.util.ArrayDeque<>();
int count = 0;
for (int r = 0; r < rows; r++) {
for (int c = 0; c < cols; c++) {
if (grid[r].charAt(c) != '1' || seen[r][c]) continue;
count++; // a new island: flood it
seen[r][c] = true;
stack.push(new int[]{r, c});
while (!stack.isEmpty()) {
int[] cell = stack.pop();
for (int k = 0; k < 4; k++) {
int a = cell[0] + dr[k], b = cell[1] + dc[k];
if (a >= 0 && a < rows && b >= 0 && b < cols && grid[a].charAt(b) == '1' && !seen[a][b]) {
seen[a][b] = true; // mark on push
stack.push(new int[]{a, b});
}
}
}
}
}
return count;
}
}function numIslands(grid) {
const rows = grid.length, cols = grid[0].length;
const seen = Array.from({ length: rows }, () => new Array(cols).fill(false));
let count = 0;
for (let r = 0; r < rows; r++) {
for (let c = 0; c < cols; c++) {
if (grid[r][c] !== '1' || seen[r][c]) continue;
count++; // a new island: flood it
seen[r][c] = true;
const stack = [[r, c]];
while (stack.length) {
const [i, j] = stack.pop();
for (const [a, b] of [[i + 1, j], [i - 1, j], [i, j + 1], [i, j - 1]]) {
if (a >= 0 && a < rows && b >= 0 && b < cols && grid[a][b] === '1' && !seen[a][b]) {
seen[a][b] = true; // mark on push
stack.push([a, b]);
}
}
}
}
}
return count;
}fn solve(grid: &[&str]) -> i64 {
let g: Vec<&[u8]> = grid.iter().map(|row| row.as_bytes()).collect();
let (rows, cols) = (g.len(), g[0].len());
let mut seen = vec![vec![false; cols]; rows];
let mut count = 0;
for r in 0..rows {
for c in 0..cols {
if g[r][c] != b'1' || seen[r][c] {
continue;
}
count += 1; // a new island: flood it
seen[r][c] = true;
let mut stack = vec![(r, c)];
while let Some((i, j)) = stack.pop() {
let mut next = Vec::with_capacity(4);
if i > 0 { next.push((i - 1, j)); }
if i + 1 < rows { next.push((i + 1, j)); }
if j > 0 { next.push((i, j - 1)); }
if j + 1 < cols { next.push((i, j + 1)); }
for (a, b) in next {
if g[a][b] == b'1' && !seen[a][b] {
seen[a][b] = true; // mark on push
stack.push((a, b));
}
}
}
}
}
count
}import qualified Data.Set as S
-- Every unseen land cell starts a new island; flood it before moving on.
solve :: [String] -> Int
solve grid = go (S.toList land) S.empty 0
where
land = S.fromList [(r, c) | (r, row) <- zip [0 :: Int ..] grid, (c, ch) <- zip [0 :: Int ..] row, ch == '1']
go [] _ n = n
go (p : ps) seen n
| p `S.member` seen = go ps seen n
| otherwise = go ps (flood [p] (S.insert p seen)) (n + 1)
flood [] seen = seen
flood ((r, c) : st) seen =
let next = [q | q <- [(r + 1, c), (r - 1, c), (r, c + 1), (r, c - 1)], q `S.member` land, not (q `S.member` seen)]
in flood (next ++ st) (foldr S.insert seen next)
Variants to try
- Return the size of the largest island instead of the count.
- Count islands with a union-find structure, and explain when it beats a search (cells arriving one at a time).
- Count the distinct island shapes, up to translation.
- Land is added one cell at a time; report the number of islands after each addition.
Next problems
The largest island
Return the number of cells in the largest island of a 0/1 grid: count each component's cells and keep the maximum.
Open the problem → Medium15 minCount the friend groups
People know each other directly or through friends. Count the separate groups, given an n × n matrix of who knows whom.
Open the problem → Medium15 minThe cheapest node in each group
Each node has a value. Find the smallest value in every connected group and return their sum: the components template with f = min and g = sum.
Open the problem →Write a solution and run it against the real test table.
Keep going
The largest island
Return the number of cells in the largest island of a 0/1 grid: count each component's cells and keep the maximum.
Open the problem → Medium15 minCount the friend groups
People know each other directly or through friends. Count the separate groups, given an n × n matrix of who knows whom.
Open the problem → Medium15 minThe cheapest node in each group
Each node has a value. Find the smallest value in every connected group and return their sum: the components template with f = min and g = sum.
Open the problem → GraphsThe pattern behind it
Grids, networks and dependencies are all graphs. Breadth-first search finds shortest paths, depth-first search and union-find find what is connected, Dijkstra handles weighted edges, and a queue of nodes with no remaining prerequisites gives an order.
Read the pattern →Preparing for a real process? Quant interview preparation, or book a free 20-minute call.