Number of Islands

You are given a rectangular map grid where each cell is the character "1" (land) or "0" (water). An island is a group of land cells connected to each other horizontally or vertically; land cells that only touch at a corner belong to different islands.

Return the number of islands on the map. Everything outside the grid counts as water.

Example 1

Input: grid = [["1","1","0","0","0"],["1","1","0","1","0"],["0","0","0","0","0"],["0","0","0","1","1"]] Output: 3 Why: The 2x2 block in the top-left corner, the lone cell in the second row and the pair of cells in the bottom-right corner form three separate islands.

Example 2

Input: grid = [["0","0","0"],["0","0","0"]] Output: 0 Why: There is no land, so there are no islands.

Example 3

Input: grid = [["1","0","0"],["0","1","0"],["0","0","1"]] Output: 3 Why: Land cells touching only diagonally are separate islands: here there are three.

Constraints

  • 1 <= m, n <= 100 (m rows, n columns)
  • grid[i][j] is "0" or "1"

Pattern Path · Graphs: Traversal · problem 131 of 169

Think of each land cell as a graph node with edges to its land neighbours up, down, left and right. What is an island in graph terms?
An island is a connected component. Scan the grid; whenever you find land you have not visited yet, you have found a new island.
From that cell, flood-fill the whole island (DFS or BFS) and mark every cell you reach as visited so it is never counted again.
Target: O(m · n) time, O(m · n) in the worst case for the queue extra space.

Start simple

For each land cell, run a search to collect its island and compare it with the islands found so far: O((mn)²) time.

The key idea

Treat the grid as a graph whose nodes are land cells and whose edges join land cells that share a side. Islands are then exactly the connected components, and counting components is a classic traversal task.

Scan every cell in row order. When you reach a land cell that has not been visited, increase the counter and flood-fill from it: use a queue (BFS) or stack (DFS) to visit every land cell reachable from it, marking each as visited as soon as it is added. When the fill finishes, the whole island is marked, so the scan will skip all of its other cells and the island is counted exactly once.

Marking can be done by overwriting the cell with "0" (sinking the island) or with a separate visited matrix if the input must stay unchanged. Each cell is added to the queue at most once and checks four neighbours, so the total work is linear in the grid size. An explicit queue or stack avoids hitting recursion limits on large, snake-shaped islands; union-find is another valid way to merge neighbouring land cells.

Complexity

  • Time: O(m · n)
  • Space: O(m · n) in the worst case for the queue

Python

class Solution:
    def numIslands(self, grid: List[List[str]]) -> int:
        rows, cols = len(grid), len(grid[0])
        islands = 0

        for r in range(rows):
            for c in range(cols):
                if grid[r][c] != "1":
                    continue
                # New island found: sink all of it so it is counted only once.
                islands += 1
                grid[r][c] = "0"
                queue = deque([(r, c)])
                while queue:
                    i, j = queue.popleft()
                    for ni, nj in ((i + 1, j), (i - 1, j), (i, j + 1), (i, j - 1)):
                        if 0 <= ni < rows and 0 <= nj < cols and grid[ni][nj] == "1":
                            grid[ni][nj] = "0"  # mark when pushed, not when popped
                            queue.append((ni, nj))

        return islands

JavaScript

var numIslands = function(grid) {
  const rows = grid.length, cols = grid[0].length;
  const dirs = [[1, 0], [-1, 0], [0, 1], [0, -1]];
  let islands = 0;

  for (let r = 0; r < rows; r++) {
    for (let c = 0; c < cols; c++) {
      if (grid[r][c] !== "1") continue;
      // New island found: sink all of it so it is counted only once.
      islands++;
      grid[r][c] = "0";
      const stack = [[r, c]];
      while (stack.length > 0) {
        const [i, j] = stack.pop();
        for (const [di, dj] of dirs) {
          const ni = i + di, nj = j + dj;
          if (ni >= 0 && ni < rows && nj >= 0 && nj < cols && grid[ni][nj] === "1") {
            grid[ni][nj] = "0"; // mark when pushed, not when popped
            stack.push([ni, nj]);
          }
        }
      }
    }
  }
  return islands;
};

What the interviewer is checking

It is the standard test of recognising connected components in a grid and writing a bug-free flood fill with correct bounds checks.

Mistakes to avoid

  • Counting diagonal neighbours as connected.
  • Marking cells visited only when they are popped instead of when they are pushed, so the same cell is queued many times.
  • Comparing with the number 1 instead of the string "1".
  • Deep recursion on a long winding island can overflow the stack.

Follow-ups to expect

  • Return the size of the largest island? (Count cells during the fill.)
  • Land is added one cell at a time; report counts after each? (Union-find.)
  • Count islands with distinct shapes? (Normalised path signatures.)

In real software

Image processing labels connected regions (blobs) in a bitmap with this flood fill, and map software uses it to find contiguous areas like lakes or zones.

Practice next

Next Post Previous Post
No Comment
Add Comment
comment url