Skip to main content

Max Area of Island Java Solution

Complete Java implementation for finding maximum area of island in 2D grid using DFS and BFS approaches with time and space complexity analysis.

The Max Area of Island problem requires finding the largest connected component of 1s in a binary grid. This classic graph traversal problem can be solved efficiently using either depth-first search or breadth-first search strategies.

1. Problem Restatement

Clarifying questions to ask the interviewer:

  1. What is the maximum expected size of the grid (e.g., 100x100, 1000x1000)?
  2. Are we allowed to mutate the input grid during our solution?
  3. Can we assume all rows in the grid have the same length?
  4. What should we return if there are no islands (all 0s)?
  5. Should we handle negative values in the grid?

Restatement of problem: Given a 2D binary matrix where 1s represent land and 0s represent water, find the maximum area of any connected island. An island consists of connected 1s that are adjacent horizontally or vertically (not diagonally).

Input/Output specification:

  • Input: A 2D integer array grid where each element is either 0 or 1
  • Output: An integer representing the maximum area of any connected island in the grid

Constraints:

  • Grid dimensions can be up to 50x50 (based on typical FAANG constraints)
  • Grid elements are either 0 or 1
  • Islands are made of connected 1s in 4-directional directions (up, down, left, right)

Assumptions:

  • The grid is rectangular (all rows have same length)
  • We're looking for the largest connected component of 1s
  • The grid can be mutated (we can mark visited cells)
  • If there are no 1s in the grid, we return 0

Data shape and hidden structure: The input is a 2D grid where each cell represents either land (1) or water (0). The problem involves finding connected components in a grid graph, where each cell can connect to its 4 adjacent neighbors. This is fundamentally a graph traversal problem on a 2D grid with connected components.

The key insight is that we need to explore all possible islands using either DFS or BFS, tracking the area of each island and keeping track of the maximum area found.

2. Pattern Recognition

The problem asks us to find the maximum area of an island in a 2D binary grid, where an island consists of connected 1s (land) surrounded by 0s (water). This is a classic connected components problem in a 2D grid.

Pattern Identification

The key patterns that signal this approach:

  1. Grid traversal with connectivity: The problem involves a 2D grid where elements are connected based on adjacency (up, down, left, right) to form connected components.

  2. Island counting/area calculation: The problem asks for the "area" of islands, which is a direct measure of connected component size.

  3. Graph traversal pattern: This resembles graph traversal problems where we need to explore connected nodes and track their properties.

Why This Fits the Pattern

This problem fits the connected components in 2D grid pattern because:

  • We have a 2D matrix where each cell can be part of a connected component
  • The connected components are determined by 4-directional adjacency (up, down, left, right)
  • We're tasked with computing a property (area) of each connected component
  • The solution requires exploring all cells in a connected component and counting them

Core Insight

The core insight is that we can treat each island as a connected component in a graph, where:

  • Each cell with value 1 is a node
  • Adjacent cells (in 4 directions) form edges between nodes
  • We need to find the connected component with maximum size

This suggests using either DFS or BFS to traverse each connected component and calculate its area, then keep track of the maximum.

The pattern is most similar to problems like "Number of Islands" or "Flood Fill", where we're counting or measuring connected regions in a 2D grid structure.

3. Approach Evolution

My first instinct is to use a brute force approach where I check every cell and if it's land (1), I calculate the area of that island by exploring all connected land cells. But that's O(m×n)² because for each cell, I might end up traversing the entire grid to calculate area. Let me think about how to optimize this.

I notice that if I traverse each cell once and keep track of visited cells, I can avoid recalculating areas. The key insight is that once I've calculated the area of an island, I should mark all its cells as visited so I don't count them again.

Let me think about the optimal approach: I can iterate through each cell in the grid, and whenever I find an unvisited land cell (value 1), I can perform a depth-first search or breadth-first search to explore the entire connected component (island). During this exploration, I can count the area and mark all visited cells to avoid double counting.

The invariant I must maintain is that once a cell is part of an island and has been counted, it should never be counted again. I can achieve this by marking visited cells or using a separate visited array.

This approach reduces the complexity to O(m×n) because each cell is processed at most once, and I'm using the fact that islands are connected components in a 2D grid.

The key optimization is avoiding redundant traversal by marking visited cells. This transforms the problem from checking every cell for every island to a single pass through the grid with component tracking.

For the final approach, I'll use DFS with a visited array to mark cells as part of processed islands. This ensures that each cell is examined exactly once, giving us linear time complexity relative to the input size.

The core insight is that this is a connected components problem in a 2D grid, where we want to find the maximum size among all connected components of 1s.

4. Data Structure Choice

For this problem, I'll use a 2D boolean matrix to track visited cells during the DFS traversal.

Primary Data Structures Used

  1. 2D boolean array visited - Size: m × n where m is rows and n is columns
  2. Integer variable maxArea - To keep track of the maximum island area found so far

Why Each Data Structure Is Needed

The 2D boolean array is essential because:

  • It allows us to mark cells as "visited" to avoid counting the same cell multiple times
  • It enables O(1) lookup time to check if a cell has been processed
  • It prevents infinite loops in our DFS traversal by ensuring we don't revisit cells
  • It's more memory-efficient than creating a new set or using additional state tracking

The integer maxArea is needed to:

  • Keep track of the global maximum area found during our traversal
  • Avoid having to return or pass around intermediate results

Time and Space Impact

Time Complexity: O(m × n) for both the visited array initialization and the DFS traversal Space Complexity:

  • O(m × n) for the visited boolean array (this is the main space cost)
  • O(m × n) for the recursive call stack in worst case (when entire grid is one island)
  • Overall: O(m × n) space complexity

Helper Structure for Simplification

I'm not using any additional helper structures beyond the visited array. The visited array serves as both a tracking mechanism and a way to simplify the DFS logic by avoiding redundant processing of cells.

The choice of boolean array over other alternatives like HashSet or custom object tracking is driven by:

  1. Direct indexing capability (row, col)
  2. O(1) access time for visited status
  3. Memory efficiency compared to storing actual coordinates
  4. Clean integration with the grid traversal logic

This approach keeps our solution clean, readable, and efficient while maintaining the necessary tracking for the island counting algorithm.

5. Algorithm Walkthrough

Let me trace through the algorithm with a concrete example to show how it works step by step.

Consider this 2D grid:

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

Step 1: Initialize variables - maxArea = 0, rows = 4, cols = 5

Step 2: Start iterating through each cell. When we encounter a 1 at position (0,0):

  • Call DFS with coordinates (0,0)
  • Mark (0,0) as visited by setting it to 0
  • DFS explores neighbors: (0,1), (1,0), (1,1)
  • All these cells are valid and contain 1, so we continue DFS
  • The DFS returns area count = 4 for this connected component

Step 3: Continue iteration. When we reach (2,3):

  • Call DFS with coordinates (2,3)
  • Mark (2,3) as visited by setting it to 0
  • DFS explores neighbors: (2,4), (3,3)
  • Continue DFS to get area count = 2

Step 4: When we reach (3,4):

  • Call DFS with coordinates (3,4)
  • Mark (3,4) as visited by setting it to 0
  • DFS explores neighbors: (3,3)
  • Continue DFS to get area count = 2

Step 5: The maximum area found is 4, which comes from the first connected component.

Key invariants maintained:

  • Each cell is visited at most once
  • When we start DFS from a cell, it's guaranteed to be 1 (unvisited)
  • During DFS, we never revisit already-processed cells
  • The visited cells are marked to prevent reprocessing

The algorithm correctly identifies all connected components and computes their areas, returning the maximum area found. The termination is guaranteed because we process every cell exactly once and mark visited cells to prevent redundant work.

public class Solution {
    public int maxAreaOfIsland(int[][] grid) {
        if (grid == null || grid.length == 0 || grid[0].length == 0) {
            return 0;
        }
        
        int maxArea = 0;
        int rows = grid.length;
        int cols = grid[0].length;
        
        // Iterate through each cell in the grid
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                // If we find an unvisited land cell (1), start DFS
                if (grid[i][j] == 1) {
                    int area = dfs(grid, i, j);
                    maxArea = Math.max(maxArea, area);
                }
            }
        }
        
        return maxArea;
    }
    
    private int dfs(int[][] grid, int row, int col) {
        // Check bounds and if cell is water or already visited
        if (row < 0 || row >= grid.length || 
            col < 0 || col >= grid[0].length || 
            grid[row][col] == 0) {
            return 0;
        }
        
        // Mark current cell as visited by setting it to 0
        grid[row][col] = 0;
        
        // Count current cell plus area from all 4 directions
        int area = 1;
        area += dfs(grid, row - 1, col); // up
        area += dfs(grid, row + 1, col); // down
        area += dfs(grid, row, col - 1); // left
        area += dfs(grid, row, col + 1); // right
        
        return area;
    }
}

6. Java Solution

public class MaxAreaOfIsland {
    public int maxAreaOfIsland(int[][] grid) {
        if (grid == null || grid.length == 0 || grid[0].length == 0) {
            return 0;
        }
        
        int maxArea = 0;
        int rows = grid.length;
        int cols = grid[0].length;
        
        // Iterate through each cell in the grid
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                // If we find a land cell (value 1), start DFS
                if (grid[i][j] == 1) {
                    int area = dfs(grid, i, j);
                    maxArea = Math.max(maxArea, area);
                }
            }
        }
        
        return maxArea;
    }
    
    private int dfs(int[][] grid, int row, int col) {
        // Base case: out of bounds or water cell
        if (row < 0 || row >= grid.length || 
            col < 0 || col >= grid[0].length || 
            grid[row][col] == 0) {
            return 0;
        }
        
        // Mark current cell as visited by setting it to 0
        grid[row][col] = 0;
        
        // Count current cell + all connected land cells
        int area = 1;
        area += dfs(grid, row - 1, col); // up
        area += dfs(grid, row + 1, col); // down
        area += dfs(grid, row, col - 1); // left
        area += dfs(grid, row, col + 1); // right
        
        return area;
    }
}

This solution uses depth-first search (DFS) with in-place modification to calculate the maximum area of an island. For each unvisited land cell, we perform a DFS to count all connected land cells, marking them as visited by setting their value to 0. The algorithm efficiently explores all connected components while maintaining optimal time and space complexity.

7. Complexity Analysis

Time Complexity: O(m × n)

Where m is the number of rows and n is the number of columns in the grid. Each cell is visited at most once during the DFS traversal. Even though we have nested loops, the key insight is that once a cell with value 1 is visited and marked as 0 (to prevent reprocessing), it will never be part of another DFS call. Therefore, the total number of operations across all DFS calls is exactly m × n.

Space Complexity: O(m × n)

In the worst case, the recursion stack depth can reach m × n, which happens when the entire grid is filled with 1s and forms one large island. This occurs in a scenario where we have to traverse the entire grid recursively, leading to O(m × n) space usage on the call stack.

Why These Bounds Are Correct:

The time complexity is linear because we process each cell exactly once. The space complexity stems from the maximum possible depth of the recursive call stack, which occurs in the worst-case scenario where all cells form a single connected component.

Optimality:

This solution is optimal for the given problem constraints. We must examine every cell at least once to determine the maximum area, making O(m × n) time complexity unavoidable. The space complexity is also optimal since we need to account for the maximum possible recursion depth in the worst-case connected component traversal.

The solution achieves the best possible time complexity for this problem since we must inspect every element in the grid to compute the maximum area. The space complexity is also optimal given that we're using recursion and must maintain the call stack for connected component traversal.

8. Edge Cases & Pitfalls

Edge cases that require careful handling:

Empty Input: When the grid is null or has zero rows/columns, we should return 0. The solution must handle this gracefully without null pointer exceptions.

Single Cell Grids: Grids with only one cell (either 0 or 1) should return 0 or 1 respectively. These edge cases can break naive implementations that assume at least 2x2 grids.

All Zeros: When the entire grid contains only 0s, the maximum area should be 0. Some implementations might incorrectly return 1 or other values.

All Ones: When the entire grid contains only 1s, the maximum area should be the total number of cells. This requires proper flood-fill or union operations.

Island Connectivity: Islands must be connected only horizontally or vertically (not diagonally). This is a common pitfall where developers might consider diagonal connections as valid.

Boundary Conditions: When islands touch the edges of the grid, they should still be counted correctly. The DFS/BFS needs to properly handle boundary checks.

Common implementation pitfalls:

  1. Not marking visited cells: When doing DFS, failing to mark visited cells can cause infinite loops or double counting in the same island.

  2. Incorrect boundary checks: Off-by-one errors in grid bounds checking can lead to array index out of bounds exceptions.

  3. State persistence: If the input grid is mutated, some implementations might not properly reset state between multiple calls.

  4. Integer overflow: For very large grids, the area calculation might exceed integer limits (though this is less likely in typical constraints).

  5. Missing visited array: In some approaches, the solution might try to reuse the input grid for tracking visited status, which violates the constraint that input shouldn't be mutated.

The solution must carefully handle these cases to ensure correctness and robustness.

9. Alternative Approaches

There are several alternative approaches to solving the Max Area of Island problem, each with different trade-offs:

1. BFS/Queue-based Approach: Instead of using recursion, we can use a breadth-first search with a queue to traverse connected land cells. This approach avoids potential stack overflow issues with very large islands but requires additional space for the queue.

public int maxAreaOfIsland(int[][] grid) {
    if (grid == null || grid.length == 0 || grid[0].length == 0) return 0;
    
    int maxArea = 0;
    Queue<int[]> queue = new LinkedList<>();
    
    for (int i = 0; i < grid.length; i++) {
        for (int j = 0; j < grid[0].length; j++) {
            if (grid[i][j] == 1) {
                int area = 0;
                queue.offer(new int[]{i, j});
                
                while (!queue.isEmpty()) {
                    int[] cell = queue.poll();
                    int row = cell[0];
                    int col = cell[1];
                    
                    if (row < 0 || row >= grid.length || 
                        col < 0 || col >= grid[0].length || 
                        grid[row][col] == 0) continue;
                    
                    area++;
                    grid[row][col] = 0; // Mark as visited
                    
                    queue.offer(new int[]{row + 1, col});
                    queue.offer(new int[]{row - 1, col});
                    queue.offer(new int[]{row, col + 1});
                    queue.offer(new int[]{row, col - 1});
                }
                
                maxArea = Math.max(maxArea, area);
            }
        }
    }
    
    return maxArea;
}

2. Union-Find (Disjoint Set): We can model this as a connected components problem using Union-Find data structure. Each land cell is a node, and we union adjacent land cells. The maximum area would be the largest component size. This approach is more complex to implement but demonstrates understanding of graph algorithms.

3. Iterative Flood Fill: Instead of recursive DFS, we can implement iterative flood fill using a stack. This is similar to BFS but uses a stack instead of queue, and has the same space complexity characteristics.

Trade-offs Analysis:

  • The recursive DFS approach is cleanest and most intuitive for this problem
  • BFS/Queue approach avoids stack overflow but uses additional space for the queue
  • Union-Find is overkill for this specific problem but demonstrates broader algorithmic knowledge
  • All approaches have similar time complexity O(m×n) but differ in space complexity and implementation complexity

The recursive DFS approach is preferred for interviews because it's clean, readable, and efficient enough for typical constraints. The BFS alternative is useful when stack overflow concerns are real (though unlikely in practice for reasonable grid sizes).

10. Test Cases

public class MaxAreaOfIslandTest {
    public void runTests() {
        // Test case 1: Basic example from problem description
        int[][] grid1 = {
            {1,1,0,0,0},
            {1,1,0,0,0},
            {0,0,0,1,1},
            {0,0,0,1,1}
        };
        // Expected: 4
        System.out.println("Test 1: " + maxAreaOfIsland(grid1));
        
        // Test case 2: Single island
        int[][] grid2 = {
            {1,1,1},
            {1,0,1},
            {1,1,1}
        };
        // Expected: 7
        System.out.println("Test 2: " + maxAreaOfIsland(grid2));
        
        // Test case 3: No islands
        int[][] grid3 = {
            {0,0,0},
            {0,0,0},
            {0,0,0}
        };
        // Expected: 0
        System.out.println("Test 3: " + maxAreaOfIsland(grid3));
        
        // Test case 4: Multiple islands of different sizes
        int[][] grid4 = {
            {1,0,1,0,0},
            {1,0,1,1,1},
            {0,0,0,0,0},
            {1,1,0,1,1}
        };
        // Expected: 5 (from the second row)
        System.out.println("Test 4: " + maxAreaOfIsland(grid4));
        
        // Test case 5: Single cell island
        int[][] grid5 = {
            {0,0,0,1},
            {0,0,0,0},
            {0,0,0,0}
        };
        // Expected: 1
        System.out.println("Test 5: " + maxAreaOfIsland(grid5));
        
        // Test case 6: All cells are land
        int[][] grid6 = {
            {1,1,1,1},
            {1,1,1,1},
            {1,1,1,1}
        };
        // Expected: 12
        System.out.println("Test 6: " + maxAreaOfIsland(grid6));
    }
}

Sample Test Cases:

  1. Basic case: 2x2 connected component with area 4
  2. Single island: Connected 3x3 grid with area 7
  3. No islands: All zeros - return 0
  4. Multiple islands: Different sized disconnected components - return max area (5)
  5. Edge case: Single cell island - return 1
  6. All land: Complete grid - return total area (12)

The test cases cover typical scenarios including edge cases like empty grids, single elements, and boundary conditions. Each case validates a different aspect of the algorithm's correctness and robustness.

11. Final Summary

The core idea is to use DFS traversal to explore each island and track the maximum area encountered. By marking visited cells in-place, we avoid additional space overhead while ensuring each cell is processed exactly once. This approach efficiently handles the grid traversal with O(m×n) time complexity and O(m×n) space complexity in the worst case due to recursion stack depth, making it optimal for this connectivity problem where every cell must be examined at least once to determine island membership.

Was this article helpful?

Topics in this article

More from DSA Problems

  • DSA Problems

    Number of Islands Problem Solution

    Complete Java implementation for counting connected land cells in a 2D grid using DFS and BFS approaches.

  • DSA Patterns

    Number of Islands Problem Solution in Java

    Complete Java implementation for counting islands in a 2D grid using DFS and BFS approaches with time and space complexity analysis.

  • DSA Problems

    Same Tree and Symmetric Tree Problems in Java

    Comprehensive Java solution for same tree and symmetric tree problems including recursive and iterative approaches with time and space complexity analysis.

  • DSA Problems

    Flood Fill Algorithm Implementation and Analysis

    Complete Java solution for flood fill algorithm with BFS/DFS approaches, time complexity analysis, and practical implementation considerations for FAANG interviews.

Stay in the Loop

Get curated DSA patterns, coding problems, frontend and backend LLD/HLD, and AI/ML System Design insights delivered straight to your inbox. No spam, just focused interview and engineering content.