150NeetCode 150

80.Number of Islands

MediumBlind 75Graphs · 1/19

Problem

You are given an m x n grid of characters where '1' is land and '0' is water. Return the number of islands.

An island is a group of '1' cells connected horizontally or vertically (not diagonally). You may assume everything outside the grid is water.

Examples

Input:  grid = [
  ["1","1","1","1","0"],
  ["1","1","0","1","0"],
  ["1","1","0","0","0"],
  ["0","0","0","0","0"]
]
Output: 1
Explanation: All land cells touch each other through up/down/left/right moves.
Input:  grid = [
  ["1","1","0","0","0"],
  ["1","1","0","0","0"],
  ["0","0","1","0","0"],
  ["0","0","0","1","1"]
]
Output: 3
Explanation: The top-left 2x2 block, the single cell in the middle, and the pair at the bottom right.

Constraints

  • m == grid.length, n == grid[i].length
  • 1 <= m, n <= 300
  • grid[i][j] is '0' or '1'

💡 Key Insight

Treat the grid as a graph: every land cell is a node and it has an edge to each adjacent land cell. The answer is the number of connected components among land cells. Scan the grid; every time you hit land that has not been visited yet, you have found a new island — flood-fill it (DFS or BFS) so none of its cells is counted again. Each cell is touched a constant number of times, so the whole thing is O(m·n).

Brute Force

TimeO((m·n)²)SpaceO(m·n)

Intuition

Give each island exactly one "owner": its first land cell in row-major order. For every land cell, run a fresh BFS over its island and check whether any cell of the island comes earlier in row-major order. Only owners get counted, so each island is counted exactly once. It is correct but repeats the same flood fill once per cell of the island.

Algorithm

  1. For every cell (r, c) with grid[r][c] == '1':
    • BFS from (r, c) with a brand-new visited array.
    • If any reached cell has id nr * cols + nc smaller than r * cols + c, (r, c) is not the owner.
    • Otherwise increment islands.
  2. Return islands.

Visualization

grid           ids (r*cols+c)
1 1 0          0 1 2
1 0 0          3 4 5
0 0 1          6 7 8

cell 0: BFS reaches {0,1,3} → smallest id is 0 → owner   → islands = 1
cell 1: BFS reaches {1,0,3} → 0 < 1           → not owner
cell 3: BFS reaches {3,0,1} → 0 < 3           → not owner
cell 8: BFS reaches {8}     → owner                     → islands = 2

Code

java
import java.util.ArrayDeque;
import java.util.Deque;

class Solution {
    private static final int[][] DIRS = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};

    public int numIslands(char[][] grid) {
        int rows = grid.length, cols = grid[0].length;
        int islands = 0;
        for (int r = 0; r < rows; r++) {
            for (int c = 0; c < cols; c++) {
                if (grid[r][c] == '1' && isOwner(grid, r, c)) {
                    islands++;
                }
            }
        }
        return islands;
    }

    // Fresh BFS: is (r, c) the first cell (row-major) of its island?
    private boolean isOwner(char[][] grid, int r, int c) {
        int rows = grid.length, cols = grid[0].length;
        boolean[][] visited = new boolean[rows][cols];
        Deque<int[]> queue = new ArrayDeque<>();
        queue.offer(new int[]{r, c});
        visited[r][c] = true;
        int myId = r * cols + c;

        while (!queue.isEmpty()) {
            int[] cell = queue.poll();
            if (cell[0] * cols + cell[1] < myId) {
                return false; // an earlier cell owns this island
            }
            for (int[] d : DIRS) {
                int nr = cell[0] + d[0], nc = cell[1] + d[1];
                if (nr >= 0 && nr < rows && nc >= 0 && nc < cols
                        && !visited[nr][nc] && grid[nr][nc] == '1') {
                    visited[nr][nc] = true;
                    queue.offer(new int[]{nr, nc});
                }
            }
        }
        return true;
    }
}

Complexity

  • Time: O((m·n)²) — up to m·n land cells, each launching a BFS that may cover the whole grid (plus allocating a fresh visited array).
  • Space: O(m·n) — the visited array and queue of a single BFS.

Complexity Comparison

Approach Time Space
Brute Force O((m·n)²) O(m·n)
Union-Find O(m·n·α(m·n)) O(m·n)
DFS O(m·n) O(m·n)
BFS O(m·n) O(min(m, n))

Common Pitfalls

  • Comparing against the integer 1 instead of the character '1' — the grid is char[][], so grid[r][c] == 1 is always false.
  • Marking a cell visited when it is dequeued instead of when it is enqueued — the same cell gets added many times and the queue can blow up.
  • Counting diagonal neighbours as connected; only the four orthogonal directions count.
  • Forgetting the bounds check before reading grid[nr][nc], causing ArrayIndexOutOfBoundsException on edge cells.
  • Mutating the input when the caller needs it intact — if so, use a separate boolean[][] visited instead of sinking cells.