Problem
Given a 2D grid of '1's (land) and '0's (water), count how many distinct islands exist. An island is a group of land cells connected horizontally or vertically, surrounded on all sides by water or the grid boundary.
1 1 1 1 0
1 1 0 1 0
1 1 0 0 0
0 0 0 0 0
- Input: the grid above
- Output:
1 - Explanation: all nine
1-cells form one connected landmass
Counter-example showing two islands:
1 1 0 0 0
1 1 0 0 0
0 0 1 0 0
0 0 0 1 1
- Input: the grid above
- Output:
3 - Explanation: the top-left cluster, the lone center cell, and the bottom-right pair are each isolated from one another
Intuition
The problem is really asking: how many connected components made of '1' cells exist in the grid? Every time you stumble on a '1' you haven't visited yet, you've found the first cell of a brand-new island. The trick is to immediately flood-fill that entire island โ marking every connected land cell as visited โ so you never count the same island twice.
Solution โ DFS Flood Fill
Each unvisited '1' cell triggers an island count increment and a depth-first flood fill that sinks every reachable land cell by flipping it to '0'. When the DFS unwinds, all cells belonging to that island are neutralised, and the outer loop continues scanning for the next unseen island.
- If the grid is empty, return 0.
- Iterate every cell
(r, c)in the grid. - When
grid[r][c] == '1', increment the island counter and callsink_island(r, c). - In
sink_island: if the cell is out of bounds or is'0', return immediately. - Mark the cell
'0'(visited), then recurse into all four neighbours. - After the full scan, return the island counter.
1def numIslands(grid: list[list[str]]) -> int:
2 if not grid:
3 return 0
4
5 rows, cols = len(grid), len(grid[0])
6 island_count = 0
7
8 def sink_island(r: int, c: int) -> None:
9 if r < 0 or r >= rows or c < 0 or c >= cols or grid[r][c] == '0':
10 return
11 grid[r][c] = '0' # mark visited before recursing to avoid revisiting
12 sink_island(r + 1, c)
13 sink_island(r - 1, c)
14 sink_island(r, c + 1)
15 sink_island(r, c - 1)
16
17 for r in range(rows):
18 for c in range(cols):
19 if grid[r][c] == '1':
20 island_count += 1
21 sink_island(r, c)
22
23 return island_countTime: O(m ร n) โ every cell is visited at most twice: once by the outer loop and once by a flood fill.
Space: O(m ร n) โ the DFS call stack depth is bounded by the number of cells in the largest island; in the worst case (the entire grid is one snake-shaped island) this is m ร n frames.
Complexity Summary
| Approach | Time | Space | When to use |
|---|---|---|---|
| DFS Flood Fill | O(m ร n) | O(m ร n) | Standard; clean and easy to reason about |
Common Mistakes
- Marking the cell visited after recursing instead of before โ if you flip
'0'only after the four recursive calls return, each neighbour will see the current cell as unvisited and recurse back into it, causing infinite recursion. - Checking
grid[r][c]before the bounds check โgrid[r][c]throws an index-out-of-bounds error whenrorcis out of range; always validate indices first in the same condition expression. - Counting individual
'1'cells instead of connected components โ the outer loop should only incrementisland_countwhen it encounters a'1', not on every cell; the flood fill is what ensures you count groups, not cells. - Forgetting one of the four directions โ islands can be connected up, down, left, and right; dropping any direction will incorrectly split large islands into multiple smaller ones.
- Using a separate
visitedset but adding cells after the recursive calls โ the same re-entry bug as the first point; you must add(r, c)tovisitedbefore recursing into its neighbours.
Related Problems
Max Area of Islandโ same flood fill structure, but accumulate the size of each component instead of just counting itRotting Orangesโ BFS variant on a grid where the order of spreading mattersFlood Fillโ the primitive flood-fill operation in isolation, without the connected-component counting layerNumber of Provincesโ counting connected components in an adjacency matrix rather than a 2D gridSurrounded Regionsโ flood fill starting from the boundary to decide which interior regions to flip