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 1
s represent land and 0
s represent water.
An island is formed by connecting adjacent 1
s (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 1
s 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
1
s (area = 4) - Middle island with 1 cell (area = 1)
- Bottom-right island with 2 connected
1
s (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
1
s 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 connected1
s, 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.
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 1
s in four directions (up, down, left, right).
Here's the thought process:
-
Starting Point: Scan through the entire grid. Whenever you encounter a
1
, it could be part of an unexplored island. -
Island Exploration: Once you find a
1
, you need to count how many connected1
s 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
- From the current
-
Avoiding Recounting: A clever trick is to mark visited cells by changing
1
s to0
s as we explore. This serves two purposes:- Prevents infinite loops (visiting the same cell repeatedly)
- Ensures each land cell is counted only once
-
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:
-
Base Case: If the current cell is water (
grid[i][j] == 0
), return0
immediately. This handles both actual water cells and already-visited land cells. -
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. -
Area Counting: Start with
ans = 1
to count the current cell, then add the areas of all connected land cells. -
Direction Traversal: The clever use of
dirs = (-1, 0, 1, 0, -1)
withpairwise
generates the four directions:pairwise(dirs)
produces:(-1, 0)
,(0, 1)
,(1, 0)
,(0, -1)
- These represent movements: up, right, down, left
-
Boundary Checking: Before making recursive calls, check if the new coordinates
(x, y)
are within grid boundaries:0 <= x < m and 0 <= y < n
. -
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 returns0
.
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 EvaluatorExample 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
- Up: (0,1) now has
- 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
- Mark as
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
- Mark as
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 as0
(visited) - Cells with value
0
return immediately without further recursion
- When a cell with value
- Each cell is processed exactly once across all DFS calls combined
- The
pairwise(dirs)
operation generates 4 direction pairs, which isO(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 reachO(m * n)
- This happens when the island forms a snake-like pattern that requires deep recursion
- The
dirs
tuple and other local variables useO(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
In a binary min heap, the maximum element can be found in:
Recommended Readings
https assets algo monster cover_photos dfs svg Depth First Search Prereqs Recursion Review problems recursion_intro Trees problems tree_intro With a solid understanding of recursion under our belts we are now ready to tackle one of the most useful techniques in coding interviews Depth First Search DFS As the name suggests
https assets algo monster cover_photos bfs svg Breadth First Search on Trees Hopefully by this time you've drunk enough DFS Kool Aid to understand its immense power and seen enough visualization to create a call stack in your mind Now let me introduce the companion spell Breadth First Search BFS
Union Find Disjoint Set Union Data Structure Introduction Prerequisite Depth First Search Review problems dfs_intro Once we have a strong grasp of recursion and Depth First Search we can now introduce Disjoint Set Union DSU This data structure is motivated by the following problem Suppose we have sets of elements
Want a Structured Path to Master System Design Too? Donβt Miss This!