Back to Graphs
Q7Question 7 of 23

Count the Number of Islands (Grid DFS Flood Fill)

Key Concepts
  • Each unvisited land cell starts a new island; a DFS/BFS flood fill marks its entire connected region visited
  • The number of flood fills started equals the number of islands
  • Overwriting visited land with water is a neat in-place trick that avoids a separate visited array
  • Guard bounds and water cells at the start of each recursive call to keep the logic clean
  • Time is O(rows * cols) because each cell is visited at most twice; space is O(rows * cols) for the recursion stack
  • For very large grids, switch from recursive DFS to iterative BFS to avoid stack overflow

Definition

Scan the grid; each unvisited land cell starts a new island and a flood fill sinks its whole connected region so it is counted once.
  • Number of Islands treats a grid as an implicit graph where each land cell is a node connected to its four orthogonal land neighbors.
  • Flood fill (DFS or BFS) from each unvisited land cell marks the entire connected region as visited.
  • Island count equals the number of flood fills started, because each fill covers one complete island.
  • In-place marking overwrites visited land with water to avoid a separate visited array.
  • Time is O(rows * cols) visiting each cell a bounded number of times.
  • Space is O(rows * cols) worst case for the recursion stack on a single large island.

Imagine the National Bank of India plots suspicious accounts on a grid map, where a filled cell is a flagged account and neighboring flagged cells are part of the same fraud ring. A clerk sweeps the map cell by cell. The first time she touches a flagged cell she has not seen, she declares a new ring and colors in every reachable flagged cell. Each fresh untouched cluster she finds is another ring. The final tally is the number of fraud rings.

Understanding the Concepts

What is the Number of Islands problem?

Given a 2D grid of '1' (land) and '0' (water), count the number of islands. An island is a group of land cells connected horizontally or vertically (not diagonally). This is the canonical connected-components problem on a grid, solved by flood fill.

JAVA
package com.bank.service;

public class FraudRingCounter {
    public int numIslands(char[][] grid) {
        if (grid == null || grid.length == 0) return 0;
        int rows = grid.length, cols = grid[0].length;
        int count = 0;
        for (int r = 0; r < rows; r++) {
            for (int c = 0; c < cols; c++) {
                if (grid[r][c] == '1') {
                    count++;
                    floodFill(grid, r, c, rows, cols);
                }
            }
        }
        return count;
    }

    private void floodFill(char[][] grid, int r, int c,
                            int rows, int cols) {
        if (r < 0 || r >= rows || c < 0 || c >= cols
                || grid[r][c] != '1') {
            return;
        }
        grid[r][c] = '0';  // mark visited by sinking the land
        floodFill(grid, r - 1, c, rows, cols);
        floodFill(grid, r + 1, c, rows, cols);
        floodFill(grid, r, c - 1, rows, cols);
        floodFill(grid, r, c + 1, rows, cols);
    }
}

Why does each flood fill correspond to exactly one island?

When the outer loop finds an unvisited land cell, it starts a flood fill that reaches and marks every land cell connected to it. By the time the fill finishes, the entire island is marked as water. Any land the outer loop later encounters must belong to a different island, because it was not reachable from the previous start.

DIAGRAM
Grid:   1 1 0 0 0
        1 1 0 0 0
        0 0 1 0 0
        0 0 0 1 1

Flood fill from (0,0): marks {(0,0),(0,1),(1,0),(1,1)} → island 1
Flood fill from (2,2): marks {(2,2)} → island 2
Flood fill from (3,3): marks {(3,3),(3,4)} → island 3
Result: 3 islands

How does the in-place marking trick work?

Instead of maintaining a separate boolean visited array, you overwrite each visited land cell with '0' (water). This serves as the visited marker because the flood fill only processes cells that are '1'. The grid is modified in place, which is acceptable if the original grid is not needed afterward.

JAVA
// In-place marking: no extra visited array needed
grid[r][c] = '0';  // visited land becomes water

// Alternative: separate visited array
boolean[][] visited = new boolean[rows][cols];
if (!visited[r][c] && grid[r][c] == '1') {
    visited[r][c] = true;
    // ... recurse
}

What is the iterative BFS alternative?

For very large grids where a single island might span most of the grid, the recursive DFS can overflow the call stack. The iterative BFS version uses a queue and has the same logic but avoids deep recursion.

JAVA
private void floodFillBFS(char[][] grid, int r, int c,
                           int rows, int cols) {
    java.util.Queue<int[]> queue = new java.util.LinkedList<>();
    queue.offer(new int[]{r, c});
    grid[r][c] = '0';
    int[][] dirs = {{-1,0},{1,0},{0,-1},{0,1}};
    while (!queue.isEmpty()) {
        int[] cell = queue.poll();
        for (int[] d : dirs) {
            int nr = cell[0] + d[0], nc = cell[1] + d[1];
            if (nr >= 0 && nr < rows && nc >= 0 && nc < cols
                    && grid[nr][nc] == '1') {
                grid[nr][nc] = '0';
                queue.offer(new int[]{nr, nc});
            }
        }
    }
}

The Union-Find approach treats each land cell as a set element and unions adjacent land cells. Both approaches give the same island count. The DFS approach is simpler for a static grid; Union-Find shines when land is added incrementally.

| Aspect | DFS Flood Fill | Union-Find |
|--------|---------------|------------|
| Simplicity | Simpler | More complex |
| Dynamic additions | Must re-scan | O(alpha) per addition |
| Stack overflow risk | Yes for huge islands | No |
| Space | O(1) with in-place, O(R*C) stack | O(R*C) for parent/rank |

What are edge cases?

  • Empty grid: return 0.
  • All water: return 0.
  • All land: return 1 (one giant island).
  • Single cell '1': return 1.
  • Grid with only diagonal connections: those are separate islands because only horizontal and vertical connections count.
JAVA
// Edge case: diagonal connections do NOT form an island
// Grid: 1 0
//        0 1
// Result: 2 islands (not connected diagonally)

What are common mistakes?

The first mistake is marking cells at dequeue time instead of enqueue time in BFS, which causes cells to be enqueued multiple times. The second mistake is checking diagonal neighbors, which the problem does not count. The third mistake is not handling the out-of-bounds check before accessing the grid.

JAVA
// WRONG: mark at dequeue time
int[] cell = queue.poll();
grid[cell[0]][cell[1]] = '0';  // too late, may already be enqueued

// CORRECT: mark at enqueue time
grid[nr][nc] = '0';  // mark before enqueueing
queue.offer(new int[]{nr, nc});

What should a senior engineer know?

The recursive DFS can reach a call stack depth equal to the number of cells in the largest island. For a 1000x1000 grid that is entirely land, that is 1,000,000 recursive calls, which will overflow the default Java stack. Production code should use the iterative BFS or an explicit stack. The same pattern applies to flood-fill paint tools, region labeling in image processing, and connected-component analysis.

At the National Bank of India, Rajesh used this in fraud analytics where suspicious accounts plotted on a spatial grid formed clusters. Counting distinct rings let the team size its investigation effort, and the flood fill guaranteed each ring was counted exactly once.

Visual Overview

DIAGRAM
  DFS Flood Fill for Number of Islands
  ======================================

  Grid:   1 1 0      Step-by-step:
          1 0 0      1. Outer loop finds (0,0)='1'
          0 0 1         count = 1
                     2. Flood fill from (0,0):
  ┌────────────────────────────────┐
  │ (0,0)→'0', recurse:           │
  │   up: out of bounds            │
  │   down: (1,0)='1'→'0', recurse│
  │     down: (2,0)='0' stop       │
  │     right: (1,1)='0' stop     │
  │   left: out of bounds          │
  │   right: (0,1)='1'→'0'        │
  │     all neighbors ='0'→ stop   │
  └────────────────────────────────┘
                     3. Outer loop finds (2,2)='1'
                        count = 2
                        Flood fill: (2,2)→'0'
                     Result: 2 islands

Key Takeaway

Number of Islands treats the grid as an implicit graph where every land cell connects to its four orthogonal land neighbors. The algorithm scans every cell, and each unvisited land cell increments the island count and triggers a flood fill that marks its entire connected region as visited. The number of flood fills started is the number of islands.

The flood fill recursively visits each land cell, marks it as water to prevent revisiting, and recurses into its four neighbors. Overwriting the cell with water is a neat trick that avoids a separate visited array. Each cell is visited at most twice (once by the outer loop and once by the fill), so the time is O(rows * cols).

For very large grids, recursive DFS can overflow the call stack. The iterative BFS alternative uses a queue and achieves the same result without deep recursion. Both approaches correctly count islands, but BFS is production-safe for extreme grid sizes.

At the National Bank of India, Rajesh used this in fraud analytics to count clusters of suspicious accounts on a spatial grid. Each cluster represented a coordinated fraud ring, and the flood fill guaranteed each ring was counted exactly once regardless of its size or shape.

Remember one sentence: scan the grid, flood fill each new land cell, and the number of fills started is the number of islands.

---

Share
Use ← → arrow keys to navigate