Skip to main content

Max Area of Island

MEDIUMProblemSolveExternal Links

Description

You are given a two-dimensional binary matrix grid of size m x n. Each cell in the grid contains either a 1 (representing land) or a 0 (representing water).

An island is a connected group of 1s that are linked 4-directionally — meaning each land cell can connect to its neighbor directly above, below, to the left, or to the right. Diagonal connections do not count.

You may assume that all four edges of the grid are surrounded by water.

The area of an island is defined as the total number of land cells (1s) that belong to that island.

Return the maximum area among all islands in the grid. If the grid contains no islands at all, return 0.

A 5x5 grid showing two islands colored differently, with the larger island highlighted and its area labeled
A 5x5 grid showing two islands colored differently, with the larger island highlighted and its area labeled

Examples

Example 1

Input: grid = [[0,0,1,0,0,0,0,1,0,0,0,0,0],[0,0,0,0,0,0,0,1,1,1,0,0,0],[0,1,1,0,1,0,0,0,0,0,0,0,0],[0,1,0,0,1,1,0,0,1,0,1,0,0],[0,1,0,0,1,1,0,0,1,1,1,0,0],[0,0,0,0,0,0,0,0,0,0,1,0,0],[0,0,0,0,0,0,0,1,1,1,0,0,0],[0,0,0,0,0,0,0,1,1,0,0,0,0]]

Output: 6

Explanation: The grid contains several islands of varying sizes. The largest island is formed by the connected land cells at positions (3,8), (4,8), (4,9), (4,10), (5,10), and (3,10) — wait, let us trace carefully. The connected region at columns 8-10, rows 3-5 forms the largest island with area 6. Note that cells touching only diagonally are NOT considered connected, which is why the answer is 6 and not larger.

Example 2

Input: grid = [[0,0,0,0,0,0,0,0]]

Output: 0

Explanation: Every cell in the grid is water (value 0). There are no land cells at all, so no islands exist. The maximum area is 0.

Example 3

Input: grid = [[0,0,1,0,0],[0,0,1,0,0],[0,1,1,0,0],[0,0,0,0,1],[0,0,0,1,1]]

Output: 4

Explanation: There are two islands. Island A consists of cells (0,2), (1,2), (2,1), and (2,2), giving it an area of 4. Island B consists of cells (3,4), (4,3), and (4,4), giving it an area of 3. The maximum area is 4.

Constraints

  • m == grid.length
  • n == grid[i].length
  • 1 ≤ m, n ≤ 50
  • grid[i][j] is either 0 or 1

Editorial

Brute Force - DFS with Visited Matrix

Intuition

Imagine you are looking at a satellite photo of an ocean dotted with small islands. Your job is to fly a drone over the entire photo, and whenever you spot a piece of land you have not visited yet, you send the drone to explore that entire island before moving on.

The drone explores by landing on a land cell, then checking all four neighbors (up, down, left, right). If a neighbor is also land and has not been visited, the drone flies there next and repeats the process. Each time the drone visits a cell, it adds one to the island's area count.

To keep track of which cells the drone has already visited, we maintain a separate visited matrix — a grid of the same size filled with true/false values. Before exploring any cell, we check the visited matrix. After exploring, we mark it as visited.

After the drone finishes exploring one island, we record its area and move on. We scan every cell in the grid, and whenever we find an unvisited land cell, we launch a new exploration. At the end, the largest area we recorded is the answer.

This approach uses Depth-First Search (DFS) — the drone goes as deep as possible in one direction before backtracking. The visited matrix ensures we never count a cell twice.

Step-by-Step Explanation

Let's trace through with a smaller grid for clarity:

grid = [[0,1,0],
        [1,1,0],
        [0,0,1]]

We also create a visited matrix of the same size, initialized to all false.

Step 1: Start scanning from cell (0,0). Value is 0 (water). Skip it.

Step 2: Move to cell (0,1). Value is 1 (land) and not visited. Launch DFS from (0,1). Mark (0,1) as visited. Current island area = 1.

Step 3: DFS explores neighbors of (0,1). Check up (out of bounds), down (1,1) is land and unvisited → recurse. Mark (1,1) as visited. Area = 2.

Step 4: DFS from (1,1). Check up (0,1) — already visited. Check down (2,1) — value 0, water. Check left (1,0) — value 1, unvisited → recurse. Mark (1,0) as visited. Area = 3.

Step 5: DFS from (1,0). Check up (0,0) — water. Check down (2,0) — water. Check left — out of bounds. Check right (1,1) — already visited. No more unvisited neighbors. Backtrack. Island A complete with area = 3.

Step 6: Update max_area = max(0, 3) = 3. Continue scanning. Cell (0,2) is water, (1,0) visited, (1,1) visited, (1,2) water, (2,0) water, (2,1) water.

Step 7: Reach cell (2,2). Value is 1 and unvisited. Launch DFS. Mark (2,2) visited. All neighbors are water or out of bounds. Island B area = 1.

Step 8: Update max_area = max(3, 1) = 3. Scanning complete.

Result: Maximum island area = 3.

DFS Exploration with Visited Matrix — Watch how DFS explores each island cell by cell, marking visited cells and counting the area. The visited matrix prevents revisiting.

Algorithm

  1. Create a visited matrix of the same dimensions as grid, initialized to all false
  2. Initialize max_area = 0
  3. For each cell (r, c) in the grid:
    • If grid[r][c] == 1 and visited[r][c] == false:
      • Run DFS from (r, c) to compute the area of this island
      • Update max_area = max(max_area, area)
  4. DFS function dfs(r, c):
    • If (r, c) is out of bounds, or grid[r][c] == 0, or visited[r][c] == true: return 0
    • Mark visited[r][c] = true
    • Return 1 + dfs(r-1, c) + dfs(r+1, c) + dfs(r, c-1) + dfs(r, c+1)
  5. Return max_area

Code

class Solution {
public:
    int maxAreaOfIsland(vector<vector<int>>& grid) {
        int m = grid.size(), n = grid[0].size();
        vector<vector<bool>> visited(m, vector<bool>(n, false));
        int maxArea = 0;
        
        for (int r = 0; r < m; r++) {
            for (int c = 0; c < n; c++) {
                if (grid[r][c] == 1 && !visited[r][c]) {
                    int area = dfs(grid, visited, r, c, m, n);
                    maxArea = max(maxArea, area);
                }
            }
        }
        return maxArea;
    }
    
private:
    int dfs(vector<vector<int>>& grid, vector<vector<bool>>& visited,
            int r, int c, int m, int n) {
        if (r < 0 || r >= m || c < 0 || c >= n) return 0;
        if (grid[r][c] == 0 || visited[r][c]) return 0;
        
        visited[r][c] = true;
        return 1 + dfs(grid, visited, r - 1, c, m, n)
                   + dfs(grid, visited, r + 1, c, m, n)
                   + dfs(grid, visited, r, c - 1, m, n)
                   + dfs(grid, visited, r, c + 1, m, n);
    }
};
class Solution:
    def maxAreaOfIsland(self, grid: list[list[int]]) -> int:
        m, n = len(grid), len(grid[0])
        visited = [[False] * n for _ in range(m)]
        max_area = 0
        
        def dfs(r: int, c: int) -> int:
            if r < 0 or r >= m or c < 0 or c >= n:
                return 0
            if grid[r][c] == 0 or visited[r][c]:
                return 0
            
            visited[r][c] = True
            return (1 + dfs(r - 1, c) + dfs(r + 1, c)
                      + dfs(r, c - 1) + dfs(r, c + 1))
        
        for r in range(m):
            for c in range(n):
                if grid[r][c] == 1 and not visited[r][c]:
                    area = dfs(r, c)
                    max_area = max(max_area, area)
        
        return max_area
class Solution {
    public int maxAreaOfIsland(int[][] grid) {
        int m = grid.length, n = grid[0].length;
        boolean[][] visited = new boolean[m][n];
        int maxArea = 0;
        
        for (int r = 0; r < m; r++) {
            for (int c = 0; c < n; c++) {
                if (grid[r][c] == 1 && !visited[r][c]) {
                    int area = dfs(grid, visited, r, c, m, n);
                    maxArea = Math.max(maxArea, area);
                }
            }
        }
        return maxArea;
    }
    
    private int dfs(int[][] grid, boolean[][] visited,
                    int r, int c, int m, int n) {
        if (r < 0 || r >= m || c < 0 || c >= n) return 0;
        if (grid[r][c] == 0 || visited[r][c]) return 0;
        
        visited[r][c] = true;
        return 1 + dfs(grid, visited, r - 1, c, m, n)
                   + dfs(grid, visited, r + 1, c, m, n)
                   + dfs(grid, visited, r, c - 1, m, n)
                   + dfs(grid, visited, r, c + 1, m, n);
    }
}

Complexity Analysis

Time Complexity: O(m × n)

We iterate over every cell in the grid once during the main scan. Each cell is visited at most once by DFS because the visited matrix prevents re-exploration. The DFS for each cell does O(1) work (checking bounds and four neighbors). Across all DFS calls, each cell is processed at most once, giving a total of O(m × n).

Space Complexity: O(m × n)

We allocate a visited boolean matrix of size m × n, which is the dominant space cost. Additionally, the DFS recursion stack can grow up to O(m × n) in the worst case (when the entire grid is one large island shaped like a snake), though typically it is much smaller.

Why This Approach Is Not Efficient

The DFS with visited matrix approach already achieves optimal O(m × n) time complexity — we cannot do better because every cell must be examined at least once. However, its space usage is suboptimal.

The visited matrix consumes O(m × n) extra memory. For a 50 × 50 grid, that is 2,500 extra boolean values. While this is manageable for the given constraints, it is wasteful because the grid itself already contains all the information we need.

Key insight: Instead of maintaining a separate visited matrix, we can modify the grid in place. When we visit a land cell during DFS, we change its value from 1 to 0 (effectively "sinking" the island as we explore it). This way, the grid itself serves as the visited tracker — any cell that has been explored is now 0 and will naturally be skipped in future iterations.

This eliminates the entire visited matrix, reducing extra space from O(m × n) to O(1) (excluding the recursion stack, which both approaches share).

Optimal Approach - DFS with In-Place Grid Modification

Intuition

Instead of keeping a separate notebook to track which cells we have explored, we can mark them directly on the map itself. The idea is simple: when our drone visits a land cell, it "sinks" that cell by changing it from 1 to 0. This way, if any future exploration tries to visit the same cell, it will see water and stop — exactly the same effect as the visited matrix, but without any extra memory.

Think of it like a treasure hunter exploring islands on a paper map. Each time the hunter confirms they have fully explored a piece of land, they use an eraser to remove that land from the map. When scanning the map later, erased areas look like water, so the hunter naturally skips them.

The algorithm is identical to the brute force: scan every cell, launch DFS from unvisited land, count the area, and track the maximum. The only change is how we mark cells as visited — we flip them to 0 instead of using a boolean matrix.

This approach is preferred in interviews because it demonstrates space optimization awareness. The trade-off is that it modifies the input, which may not always be acceptable. If the original grid must be preserved, the visited matrix approach is the safer choice.

Step-by-Step Explanation

Let's trace through with the same grid:

grid = [[0,1,0],
        [1,1,0],
        [0,0,1]]

Step 1: Scan cell (0,0). Value is 0 (water). Skip.

Step 2: Scan cell (0,1). Value is 1 (land). Launch DFS. Sink (0,1) by setting grid[0][1] = 0. Area so far = 1.
Grid becomes: [[0,0,0],[1,1,0],[0,0,1]]

Step 3: DFS checks neighbors of (0,1). Down neighbor (1,1) is 1 → recurse. Sink (1,1): grid[1][1] = 0. Area = 2.
Grid becomes: [[0,0,0],[1,0,0],[0,0,1]]

Step 4: DFS from (1,1) checks neighbors. Left neighbor (1,0) is 1 → recurse. Sink (1,0): grid[1][0] = 0. Area = 3.
Grid becomes: [[0,0,0],[0,0,0],[0,0,1]]

Step 5: DFS from (1,0) checks all neighbors. Up (0,0) = 0, down (2,0) = 0, left = out of bounds, right (1,1) = 0 (already sunk). No unvisited land. Backtrack.

Step 6: DFS fully returns. Island A area = 3. Update max_area = 3. Continue scanning.

Step 7: Cells (0,2), (1,0), (1,1), (1,2), (2,0), (2,1) are all 0 now. Skip them all.

Step 8: Reach cell (2,2). Value is 1. Launch DFS. Sink it: grid[2][2] = 0. Area = 1. All neighbors are 0 or out of bounds.

Step 9: Island B area = 1. max_area = max(3, 1) = 3.

Result: Maximum island area = 3. The grid is now all zeros.

DFS with In-Place Sinking — Watch Islands Disappear — Watch how we sink each land cell to 0 as we visit it, eliminating the need for a separate visited matrix. The grid itself becomes the record of exploration.

Algorithm

  1. Initialize max_area = 0
  2. For each cell (r, c) in the grid:
    • If grid[r][c] == 1:
      • Run DFS from (r, c) to compute the island area
      • Update max_area = max(max_area, area)
  3. DFS function dfs(r, c):
    • If (r, c) is out of bounds or grid[r][c] == 0: return 0
    • Set grid[r][c] = 0 (sink this cell)
    • Return 1 + dfs(r-1, c) + dfs(r+1, c) + dfs(r, c-1) + dfs(r, c+1)
  4. Return max_area

Code

class Solution {
public:
    int maxAreaOfIsland(vector<vector<int>>& grid) {
        int m = grid.size(), n = grid[0].size();
        int maxArea = 0;
        
        for (int r = 0; r < m; r++) {
            for (int c = 0; c < n; c++) {
                if (grid[r][c] == 1) {
                    int area = dfs(grid, r, c, m, n);
                    maxArea = max(maxArea, area);
                }
            }
        }
        return maxArea;
    }
    
private:
    int dfs(vector<vector<int>>& grid, int r, int c, int m, int n) {
        if (r < 0 || r >= m || c < 0 || c >= n) return 0;
        if (grid[r][c] == 0) return 0;
        
        grid[r][c] = 0;  // sink the cell
        return 1 + dfs(grid, r - 1, c, m, n)
                   + dfs(grid, r + 1, c, m, n)
                   + dfs(grid, r, c - 1, m, n)
                   + dfs(grid, r, c + 1, m, n);
    }
};
class Solution:
    def maxAreaOfIsland(self, grid: list[list[int]]) -> int:
        m, n = len(grid), len(grid[0])
        max_area = 0
        
        def dfs(r: int, c: int) -> int:
            if r < 0 or r >= m or c < 0 or c >= n:
                return 0
            if grid[r][c] == 0:
                return 0
            
            grid[r][c] = 0  # sink the cell
            return (1 + dfs(r - 1, c) + dfs(r + 1, c)
                      + dfs(r, c - 1) + dfs(r, c + 1))
        
        for r in range(m):
            for c in range(n):
                if grid[r][c] == 1:
                    area = dfs(r, c)
                    max_area = max(max_area, area)
        
        return max_area
class Solution {
    public int maxAreaOfIsland(int[][] grid) {
        int m = grid.length, n = grid[0].length;
        int maxArea = 0;
        
        for (int r = 0; r < m; r++) {
            for (int c = 0; c < n; c++) {
                if (grid[r][c] == 1) {
                    int area = dfs(grid, r, c, m, n);
                    maxArea = Math.max(maxArea, area);
                }
            }
        }
        return maxArea;
    }
    
    private int dfs(int[][] grid, int r, int c, int m, int n) {
        if (r < 0 || r >= m || c < 0 || c >= n) return 0;
        if (grid[r][c] == 0) return 0;
        
        grid[r][c] = 0;  // sink the cell
        return 1 + dfs(grid, r - 1, c, m, n)
                   + dfs(grid, r + 1, c, m, n)
                   + dfs(grid, r, c - 1, m, n)
                   + dfs(grid, r, c + 1, m, n);
    }
}

Complexity Analysis

Time Complexity: O(m × n)

Every cell in the grid is visited at most twice: once during the main loop scan, and at most once during a DFS call. Once a cell is sunk to 0, it is never processed again. The total work across all DFS calls is proportional to the number of cells, giving O(m × n).

Space Complexity: O(m × n) worst case for the recursion stack

We eliminated the visited matrix, so no extra O(m × n) memory is allocated. However, the DFS recursion stack can still grow up to O(m × n) in the worst case — for example, a grid that is entirely land arranged in a snaking path. In practice, the stack depth is typically O(min(m, n)) for reasonably shaped islands.

If the original grid must be preserved (not modified), the brute force approach with the visited matrix is necessary. The in-place approach is preferred when mutation is acceptable, as it reduces auxiliary space to O(1).