Facebook Pixel

695. Max Area of Island

Problem Description

You have a 2D binary grid (matrix) where each cell contains either a 0 or a 1. The 1s represent land and 0s represent water.

An island is formed by connecting adjacent 1s (land cells) horizontally or vertically (4-directional connectivity). Diagonal connections don't count. The entire grid is surrounded by water on all four edges.

The area of an island is simply the count of 1s that form that island.

Your task is to find the largest island in the grid and return its area. If the grid contains no islands (all cells are 0), return 0.

For example, if you have a grid like:

[[1,1,0,0,0],
 [1,1,0,0,0],
 [0,0,1,0,0],
 [0,0,0,1,1]]

There are three islands:

  • Top-left island with 4 connected 1s (area = 4)
  • Middle island with 1 cell (area = 1)
  • Bottom-right island with 2 connected 1s (area = 2)

The maximum area among all islands is 4, so you would return 4.

Flowchart Walkthrough

First, let's pin down the algorithm using the Flowchart. Here's a step-by-step walkthrough:

Is it a graph?

  • Yes: The 2D grid can be viewed as a graph where each cell is a node. Adjacent cells (horizontally and vertically) with value 1 form edges between nodes. Islands are essentially connected components in this graph.

Is it a tree?

  • No: The problem involves finding connected components (islands) in a grid, which can have multiple disconnected parts and doesn't follow a tree structure.

Is the problem related to directed acyclic graphs?

  • No: The grid represents an undirected graph where connections between land cells are bidirectional.

Is the problem related to shortest paths?

  • No: We're not looking for shortest paths between points. We need to find the size of connected components (islands).

Does the problem involve connectivity?

  • Yes: The core of the problem is identifying connected components (islands) of 1s and finding their sizes.

Does the problem have small constraints?

  • Yes: The grid dimensions are typically manageable (m Γ— n grid), allowing us to visit each cell and explore islands.

DFS/backtracking?

  • Yes: DFS is perfect for exploring each island completely. When we find a 1, we can use DFS to visit all connected 1s, counting them to get the island's area.

Conclusion: The flowchart suggests using DFS (Depth-First Search) to explore each island and calculate its area. We iterate through the grid, and whenever we find an unvisited land cell (1), we perform DFS to explore the entire island while counting its cells.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

Think of the grid as a map where you're looking at islands from above. To find the largest island, you need to explore each island completely and measure its size.

The key insight is that once you land on any part of an island (a cell with 1), you want to explore the entire island to count all its land cells. This is where DFS comes in naturally - when you find a 1, you recursively explore all its adjacent 1s in four directions (up, down, left, right).

Here's the thought process:

  1. Starting Point: Scan through the entire grid. Whenever you encounter a 1, it could be part of an unexplored island.

  2. Island Exploration: Once you find a 1, you need to count how many connected 1s form this island. DFS is perfect here because:

    • From the current 1, you check all 4 adjacent cells
    • If an adjacent cell is also 1, it's part of the same island, so you continue exploring from there
    • This recursive exploration naturally covers the entire island
  3. Avoiding Recounting: A clever trick is to mark visited cells by changing 1s to 0s as we explore. This serves two purposes:

    • Prevents infinite loops (visiting the same cell repeatedly)
    • Ensures each land cell is counted only once
  4. Finding Maximum: As we explore each island and get its area, we keep track of the maximum area found so far.

The beauty of this approach is its simplicity - we're essentially doing a flood-fill operation (like the paint bucket tool in image editors) on each island, counting cells as we go. The DFS naturally handles the irregular shapes of islands and ensures we explore every connected piece of land.

The formula for each island's area is straightforward: start with 1 for the current cell, then add the areas returned by DFS calls to all valid adjacent land cells. The recursion stops when we hit water (0) or grid boundaries.

Learn more about Depth-First Search, Breadth-First Search and Union Find patterns.

Solution Approach

The implementation uses DFS with in-place modification to efficiently find the maximum island area:

Main Function Structure:

def maxAreaOfIsland(self, grid: List[List[int]]) -> int:
    m, n = len(grid), len(grid[0])
    return max(dfs(i, j) for i in range(m) for j in range(n))

The main function iterates through every cell in the grid and calls DFS on each position. It returns the maximum area found across all DFS calls.

DFS Helper Function:

def dfs(i: int, j: int) -> int:
    if grid[i][j] == 0:
        return 0
    ans = 1
    grid[i][j] = 0
    dirs = (-1, 0, 1, 0, -1)
    for a, b in pairwise(dirs):
        x, y = i + a, j + b
        if 0 <= x < m and 0 <= y < n:
            ans += dfs(x, y)
    return ans

Key Implementation Details:

  1. Base Case: If the current cell is water (grid[i][j] == 0), return 0 immediately. This handles both actual water cells and already-visited land cells.

  2. Marking Visited: The line grid[i][j] = 0 marks the current land cell as visited by converting it to water. This prevents revisiting the same cell and avoids infinite recursion.

  3. Area Counting: Start with ans = 1 to count the current cell, then add the areas of all connected land cells.

  4. Direction Traversal: The clever use of dirs = (-1, 0, 1, 0, -1) with pairwise generates the four directions:

    • pairwise(dirs) produces: (-1, 0), (0, 1), (1, 0), (0, -1)
    • These represent movements: up, right, down, left
  5. Boundary Checking: Before making recursive calls, check if the new coordinates (x, y) are within grid boundaries: 0 <= x < m and 0 <= y < n.

  6. Recursive Accumulation: For each valid adjacent cell, add its DFS result to the current area: ans += dfs(x, y). If it's water or out of bounds, the DFS returns 0.

Time Complexity: O(m Γ— n) where m and n are the grid dimensions. Each cell is visited at most once.

Space Complexity: O(m Γ— n) in the worst case for the recursion stack when the entire grid is one island.

The elegance of this solution lies in its simplicity - by modifying the grid in-place and using DFS to explore each island completely, we avoid the need for additional visited arrays or complex state management.

Ready to land your dream job?

Unlock your dream job with a 5-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through a small example to illustrate the solution approach:

Grid:
[[1, 1, 0],
 [0, 1, 0],
 [1, 0, 1]]

Step 1: Start scanning from position (0,0)

  • We find a 1 at (0,0), so we start DFS
  • Mark (0,0) as visited by setting it to 0
  • Current area = 1
  • Check 4 directions:
    • Up: Out of bounds
    • Right: (0,1) has 1, recursively call DFS
    • Down: (1,0) has 0, skip
    • Left: Out of bounds

Step 2: DFS from (0,1)

  • Mark (0,1) as 0
  • Current area = 1
  • Check 4 directions:
    • Up: Out of bounds
    • Right: (0,2) has 0, skip
    • Down: (1,1) has 1, recursively call DFS
    • Left: (0,0) now has 0 (already visited), skip

Step 3: DFS from (1,1)

  • Mark (1,1) as 0
  • Current area = 1
  • Check 4 directions:
    • Up: (0,1) now has 0 (already visited), skip
    • Right: (1,2) has 0, skip
    • Down: (2,1) has 0, skip
    • Left: (1,0) has 0, skip
  • Return 1 to Step 2

Step 4: Complete DFS from (0,1)

  • Total area from (1,1) = 1
  • Return 1 + 1 = 2 to Step 1

Step 5: Complete DFS from (0,0)

  • Total area from (0,1) = 2
  • Return 1 + 2 = 3

First island complete with area = 3

Grid now looks like:

[[0, 0, 0],
 [0, 0, 0],
 [1, 0, 1]]

Step 6: Continue scanning

  • Positions (0,1) through (1,2) all have 0, DFS returns 0
  • Position (2,0) has 1, start new DFS
    • Mark as 0, check all directions
    • All neighbors are 0 or out of bounds
    • Return area = 1

Step 7: Continue to (2,2)

  • Position (2,2) has 1, start new DFS
    • Mark as 0, check all directions
    • All neighbors are 0 or out of bounds
    • Return area = 1

Step 8: Find maximum

  • Island areas found: 3, 1, 1
  • Maximum area = 3

The grid is completely processed, and we return 3 as the maximum island area.

Solution Implementation

1class Solution:
2    def maxAreaOfIsland(self, grid: List[List[int]]) -> int:
3        """
4        Find the maximum area of an island in a 2D grid.
5        An island is a group of 1's connected horizontally or vertically.
6      
7        Args:
8            grid: 2D list of integers (0 or 1)
9      
10        Returns:
11            Maximum area of an island
12        """
13        def dfs(row: int, col: int) -> int:
14            """
15            Depth-first search to calculate the area of an island.
16            Marks visited cells as 0 to avoid revisiting.
17          
18            Args:
19                row: Current row index
20                col: Current column index
21          
22            Returns:
23                Area of the island containing the current cell
24            """
25            # Base case: if current cell is water (0), return 0
26            if grid[row][col] == 0:
27                return 0
28          
29            # Count current cell as part of island
30            area = 1
31          
32            # Mark current cell as visited by setting it to 0
33            grid[row][col] = 0
34          
35            # Define four directions: up, right, down, left
36            # Using pairwise to create direction pairs: (-1,0), (0,1), (1,0), (0,-1)
37            directions = (-1, 0, 1, 0, -1)
38          
39            # Explore all four adjacent cells
40            for delta_row, delta_col in pairwise(directions):
41                next_row = row + delta_row
42                next_col = col + delta_col
43              
44                # Check if the next cell is within grid boundaries
45                if 0 <= next_row < rows and 0 <= next_col < cols:
46                    # Recursively explore and add the area of connected land
47                    area += dfs(next_row, next_col)
48          
49            return area
50      
51        # Get grid dimensions
52        rows, cols = len(grid), len(grid[0])
53      
54        # Find maximum island area by checking all cells in the grid
55        # For each cell, if it's land (1), calculate its island area using DFS
56        return max(dfs(i, j) for i in range(rows) for j in range(cols))
57
1class Solution {
2    // Grid dimensions
3    private int rows;
4    private int cols;
5    private int[][] grid;
6
7    /**
8     * Finds the maximum area of an island in the given grid.
9     * An island is a group of 1's (representing land) connected horizontally or vertically.
10     * 
11     * @param grid 2D array where 1 represents land and 0 represents water
12     * @return The maximum area among all islands
13     */
14    public int maxAreaOfIsland(int[][] grid) {
15        // Initialize grid dimensions
16        this.rows = grid.length;
17        this.cols = grid[0].length;
18        this.grid = grid;
19      
20        int maxArea = 0;
21      
22        // Iterate through each cell in the grid
23        for (int row = 0; row < rows; row++) {
24            for (int col = 0; col < cols; col++) {
25                // Calculate area for each potential island and track maximum
26                maxArea = Math.max(maxArea, dfs(row, col));
27            }
28        }
29      
30        return maxArea;
31    }
32
33    /**
34     * Performs depth-first search to calculate the area of an island.
35     * Marks visited cells by setting them to 0.
36     * 
37     * @param row Current row index
38     * @param col Current column index
39     * @return Area of the island starting from the current cell
40     */
41    private int dfs(int row, int col) {
42        // Base case: if current cell is water, return 0
43        if (grid[row][col] == 0) {
44            return 0;
45        }
46      
47        // Count current land cell
48        int area = 1;
49      
50        // Mark current cell as visited by setting it to 0
51        grid[row][col] = 0;
52      
53        // Direction vectors for exploring 4 adjacent cells (up, right, down, left)
54        int[] directions = {-1, 0, 1, 0, -1};
55      
56        // Explore all 4 adjacent cells
57        for (int k = 0; k < 4; k++) {
58            int nextRow = row + directions[k];
59            int nextCol = col + directions[k + 1];
60          
61            // Check if the next cell is within bounds
62            if (nextRow >= 0 && nextRow < rows && nextCol >= 0 && nextCol < cols) {
63                // Recursively calculate area of connected land cells
64                area += dfs(nextRow, nextCol);
65            }
66        }
67      
68        return area;
69    }
70}
71
1class Solution {
2public:
3    int maxAreaOfIsland(vector<vector<int>>& grid) {
4        int rows = grid.size();
5        int cols = grid[0].size();
6      
7        // Direction vectors for exploring 4 adjacent cells (up, right, down, left)
8        // Using pairs: (-1,0), (0,1), (1,0), (0,-1)
9        int directions[5] = {-1, 0, 1, 0, -1};
10      
11        int maxArea = 0;
12      
13        // Define DFS lambda function to calculate island area
14        function<int(int, int)> dfs = [&](int row, int col) -> int {
15            // Base case: if current cell is water, return 0
16            if (grid[row][col] == 0) {
17                return 0;
18            }
19          
20            // Count current cell as part of island
21            int currentArea = 1;
22          
23            // Mark current cell as visited by setting it to 0
24            grid[row][col] = 0;
25          
26            // Explore all 4 adjacent cells
27            for (int k = 0; k < 4; ++k) {
28                int newRow = row + directions[k];
29                int newCol = col + directions[k + 1];
30              
31                // Check if the new position is within grid boundaries
32                if (newRow >= 0 && newRow < rows && newCol >= 0 && newCol < cols) {
33                    // Recursively add area from connected land cells
34                    currentArea += dfs(newRow, newCol);
35                }
36            }
37          
38            return currentArea;
39        };
40      
41        // Iterate through each cell in the grid
42        for (int i = 0; i < rows; ++i) {
43            for (int j = 0; j < cols; ++j) {
44                // Start DFS from each unvisited land cell and update max area
45                maxArea = max(maxArea, dfs(i, j));
46            }
47        }
48      
49        return maxArea;
50    }
51};
52
1/**
2 * Finds the maximum area of an island in a binary grid
3 * An island is a group of 1's (representing land) connected 4-directionally (horizontal or vertical)
4 * @param grid - 2D binary grid where 1 represents land and 0 represents water
5 * @returns The area of the largest island in the grid
6 */
7function maxAreaOfIsland(grid: number[][]): number {
8    const rows: number = grid.length;
9    const cols: number = grid[0].length;
10  
11    // Direction array for exploring 4 adjacent cells (up, right, down, left)
12    // Using a condensed format: [dx1, dy1, dx2, dy2, dx3, dy3, dx4, dy4, dx1]
13    const directions: number[] = [-1, 0, 1, 0, -1];
14  
15    /**
16     * Depth-first search to calculate the area of an island starting from position (row, col)
17     * Marks visited cells as 0 to avoid revisiting
18     * @param row - Current row index
19     * @param col - Current column index
20     * @returns The area of the island connected to this cell
21     */
22    const depthFirstSearch = (row: number, col: number): number => {
23        // Base case: if current cell is water (0), return 0
24        if (grid[row][col] === 0) {
25            return 0;
26        }
27      
28        // Count current cell as part of island area
29        let currentArea: number = 1;
30      
31        // Mark current cell as visited by setting it to 0
32        grid[row][col] = 0;
33      
34        // Explore all 4 adjacent directions
35        for (let dirIndex = 0; dirIndex < 4; dirIndex++) {
36            const nextRow: number = row + directions[dirIndex];
37            const nextCol: number = col + directions[dirIndex + 1];
38          
39            // Check if the next position is within grid boundaries
40            if (nextRow >= 0 && nextRow < rows && nextCol >= 0 && nextCol < cols) {
41                // Recursively calculate area from adjacent cells
42                currentArea += depthFirstSearch(nextRow, nextCol);
43            }
44        }
45      
46        return currentArea;
47    };
48  
49    let maxArea: number = 0;
50  
51    // Iterate through each cell in the grid
52    for (let row = 0; row < rows; row++) {
53        for (let col = 0; col < cols; col++) {
54            // Start DFS from each unvisited land cell and update maximum area
55            maxArea = Math.max(maxArea, depthFirstSearch(row, col));
56        }
57    }
58  
59    return maxArea;
60}
61

Time and Space Complexity

Time Complexity: O(m * n) where m is the number of rows and n is the number of columns in the grid.

  • The main function iterates through every cell in the grid using the generator expression, which takes O(m * n) time.
  • For each cell, if it contains a 1, the DFS function is called.
  • The DFS function visits each cell at most once because:
    • When a cell with value 1 is visited, it's immediately marked as 0 (visited)
    • Cells with value 0 return immediately without further recursion
  • Each cell is processed exactly once across all DFS calls combined
  • The pairwise(dirs) operation generates 4 direction pairs, which is O(1)
  • Therefore, the total time complexity is O(m * n)

Space Complexity: O(m * n) in the worst case.

  • The space complexity is dominated by the recursion call stack
  • In the worst case, when the entire grid forms a single island (all cells are 1), the DFS recursion depth could reach O(m * n)
  • This happens when the island forms a snake-like pattern that requires deep recursion
  • The dirs tuple and other local variables use O(1) space
  • The generator expression in the main function uses O(1) additional space
  • Therefore, the overall space complexity is O(m * n) due to the recursion stack

Learn more about how to find time and space complexity quickly.

Common Pitfalls

1. Modifying the Original Grid

The most significant pitfall in this solution is that it modifies the input grid in-place by setting visited cells to 0. This destructive modification means:

  • The original grid data is lost after running the function
  • The function cannot be called multiple times on the same grid
  • This violates the principle of not modifying input parameters unless explicitly required

Solution: Create a copy of the grid or use a separate visited set to track visited cells:

def maxAreaOfIsland(self, grid: List[List[int]]) -> int:
    rows, cols = len(grid), len(grid[0])
    visited = set()
  
    def dfs(row: int, col: int) -> int:
        if (row, col) in visited or grid[row][col] == 0:
            return 0
      
        visited.add((row, col))
        area = 1
      
        for dr, dc in [(-1, 0), (0, 1), (1, 0), (0, -1)]:
            next_row, next_col = row + dr, col + dc
            if 0 <= next_row < rows and 0 <= next_col < cols:
                area += dfs(next_row, next_col)
      
        return area
  
    max_area = 0
    for i in range(rows):
        for j in range(cols):
            if grid[i][j] == 1 and (i, j) not in visited:
                max_area = max(max_area, dfs(i, j))
  
    return max_area

2. Missing pairwise Import

The code uses pairwise without importing it. This function is only available in Python 3.10+ from itertools.

Solution: Either import it or use a simpler direction list:

from itertools import pairwise  # Add this import

# Or replace the direction logic with:
directions = [(-1, 0), (0, 1), (1, 0), (0, -1)]
for delta_row, delta_col in directions:
    # ... rest of the code

3. Inefficient Maximum Calculation

The current approach calls DFS on every cell, including water cells (0s) and already-visited land cells, which immediately return 0. This creates unnecessary function calls.

Solution: Check if a cell is worth exploring before calling DFS:

def maxAreaOfIsland(self, grid: List[List[int]]) -> int:
    rows, cols = len(grid), len(grid[0])
    max_area = 0
  
    for i in range(rows):
        for j in range(cols):
            if grid[i][j] == 1:  # Only call DFS on unvisited land
                max_area = max(max_area, dfs(i, j))
  
    return max_area

4. Stack Overflow Risk

For very large islands, the recursive DFS approach could cause stack overflow due to deep recursion.

Solution: Use an iterative approach with an explicit stack:

def dfs_iterative(row: int, col: int) -> int:
    if grid[row][col] == 0:
        return 0
  
    stack = [(row, col)]
    area = 0
  
    while stack:
        r, c = stack.pop()
        if grid[r][c] == 0:
            continue
          
        grid[r][c] = 0
        area += 1
      
        for dr, dc in [(-1, 0), (0, 1), (1, 0), (0, -1)]:
            nr, nc = r + dr, c + dc
            if 0 <= nr < rows and 0 <= nc < cols and grid[nr][nc] == 1:
                stack.append((nr, nc))
  
    return area
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

In a binary min heap, the maximum element can be found in:


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More