463. Island Perimeter
Problem Description
You have a 2D grid that represents a map. Each cell in the grid contains either:
1
representing land0
representing water
The grid forms an island with these characteristics:
- There is exactly one island (a group of connected land cells)
- Land cells connect only horizontally or vertically (not diagonally)
- The entire grid is surrounded by water
- The island has no "lakes" (no water cells completely enclosed within the island)
Your task is to calculate the total perimeter of this island.
The perimeter is the total number of edges where land meets water. Each cell is a square with side length 1, so each exposed edge contributes 1 to the perimeter.
For example, a single land cell 1
surrounded by water would have a perimeter of 4 (all four sides exposed). When land cells are adjacent, they share edges, reducing the total perimeter.
Constraints:
- Grid dimensions don't exceed 100x100
- The grid is rectangular
- There is exactly one island in the grid
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 grid can be viewed as a graph where each cell is a node, and adjacent cells (horizontally/vertically) form edges. We need to explore connected land cells.
Is it a tree?
- No: The grid structure is not a tree. It's a 2D grid where cells can have multiple connections and there's no hierarchical parent-child relationship.
Is the problem related to directed acyclic graphs?
- No: The grid connections are undirected (you can move between adjacent cells in both directions), and it's not about directed relationships.
Is the problem related to shortest paths?
- No: We're not finding the shortest path between points. We're calculating the perimeter of the island.
Does the problem involve connectivity?
- Yes: The problem involves exploring connected land cells to determine the island's boundaries and calculate its perimeter.
Does the problem have small constraints?
- Yes: The grid dimensions don't exceed 100x100, which is relatively small and allows for exhaustive exploration.
DFS/backtracking?
- Yes: DFS is suitable for exploring all connected land cells and marking boundaries where land meets water.
Conclusion: The flowchart suggests using DFS (Depth-First Search) to explore the connected land cells and identify the perimeter. DFS can traverse through all land cells, and for each land cell, we can check its adjacent cells to determine how many edges contribute to the perimeter.
Intuition
The key insight is that each land cell starts with 4 potential edges that could contribute to the perimeter. However, when a land cell shares an edge with another land cell, that shared edge doesn't count toward the perimeter - it's internal to the island.
Think of it this way: imagine placing a single 1x1
square on a table. It has 4 exposed edges, contributing 4 to the perimeter. Now place another square next to it. Each square still has 4 edges, but where they touch, those 2 edges (one from each square) are no longer part of the perimeter - they're internal boundaries.
This leads to a simple counting strategy:
- For each land cell we find, add 4 to our perimeter count (assuming all edges are exposed)
- Then check if this land cell has any adjacent land cells
- For each adjacent land cell, we subtract the shared edges from our count
The clever part is recognizing that when two land cells share an edge, we need to subtract 2 from our total - one edge from each cell that's no longer exposed. To avoid double-counting these subtractions, we only check in two directions (right and down) rather than all four directions. This way, each shared edge is counted exactly once.
For example, if we have two adjacent land cells:
- Cell A contributes
+4
to perimeter - Cell B contributes
+4
to perimeter - They share an edge, so we subtract
2
(one from each) - Total contribution:
4 + 4 - 2 = 6
This approach eliminates the need for complex DFS traversal or visited tracking - we simply iterate through the grid once, counting and adjusting as we go.
Solution Approach
The implementation uses a simple grid traversal with a counting strategy:
1. Initialize the Grid Dimensions and Counter
m, n = len(grid), len(grid[0])
ans = 0
We store the grid dimensions and initialize our perimeter counter to 0.
2. Traverse Each Cell in the Grid
for i in range(m):
for j in range(n):
We iterate through every cell in the grid using nested loops.
3. Check if Current Cell is Land
if grid[i][j] == 1: ans += 4
When we find a land cell (1
), we initially assume all 4 of its edges contribute to the perimeter, so we add 4 to our counter.
4. Check for Adjacent Land Cells (Right)
if i < m - 1 and grid[i + 1][j] == 1: ans -= 2
If there's a land cell directly below the current cell (checking bounds first with i < m - 1
), these two cells share an edge. We subtract 2 from our counter because:
- The current cell's bottom edge is not part of the perimeter
- The cell below's top edge is not part of the perimeter
5. Check for Adjacent Land Cells (Down)
if j < n - 1 and grid[i][j + 1] == 1: ans -= 2
Similarly, if there's a land cell to the right (checking bounds with j < n - 1
), we subtract 2 for the shared vertical edge.
Why Only Check Right and Down?
By only checking in two directions instead of all four, we ensure each shared edge is counted exactly once. When we're at cell (i,j)
and check cell (i+1,j)
, we handle that shared edge. Later, when we reach cell (i+1,j)
, we don't need to check upward because we've already accounted for that shared edge.
Time Complexity: O(m ร n)
where m and n are the grid dimensions, as we visit each cell once.
Space Complexity: O(1)
as we only use a constant amount of extra space for variables.
Ready to land your dream job?
Unlock your dream job with a 3-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's trace through a small example to illustrate the solution approach:
Grid: [ [0, 1, 0], [1, 1, 1], [0, 1, 0] ]
This forms a plus-shaped island. Let's calculate its perimeter step by step:
Initial state: ans = 0
Row 0, Column 0: grid[0][0] = 0
(water) โ skip
Row 0, Column 1: grid[0][1] = 1
(land)
- Add 4 to perimeter:
ans = 0 + 4 = 4
- Check right
(0,2)
: water โ no adjustment - Check down
(1,1)
: land โ subtract 2:ans = 4 - 2 = 2
Row 0, Column 2: grid[0][2] = 0
(water) โ skip
Row 1, Column 0: grid[1][0] = 1
(land)
- Add 4 to perimeter:
ans = 2 + 4 = 6
- Check right
(1,1)
: land โ subtract 2:ans = 6 - 2 = 4
- Check down
(2,0)
: water โ no adjustment
Row 1, Column 1: grid[1][1] = 1
(land)
- Add 4 to perimeter:
ans = 4 + 4 = 8
- Check right
(1,2)
: land โ subtract 2:ans = 8 - 2 = 6
- Check down
(2,1)
: land โ subtract 2:ans = 6 - 2 = 4
Row 1, Column 2: grid[1][2] = 1
(land)
- Add 4 to perimeter:
ans = 4 + 4 = 8
- Check right: out of bounds โ no adjustment
- Check down
(2,2)
: water โ no adjustment
Row 2, Column 0: grid[2][0] = 0
(water) โ skip
Row 2, Column 1: grid[2][1] = 1
(land)
- Add 4 to perimeter:
ans = 8 + 4 = 12
- Check right
(2,2)
: water โ no adjustment - Check down: out of bounds โ no adjustment
Row 2, Column 2: grid[2][2] = 0
(water) โ skip
Final perimeter: 12
To verify: The plus shape has 5 land cells. Each cell initially contributes 4 edges (5 ร 4 = 20). There are 4 shared edges (each removing 2 from the count), so: 20 - (4 ร 2) = 12 โ
Solution Implementation
1class Solution:
2 def islandPerimeter(self, grid: List[List[int]]) -> int:
3 """
4 Calculate the perimeter of an island in a 2D grid.
5
6 Args:
7 grid: 2D list where 1 represents land and 0 represents water
8
9 Returns:
10 The total perimeter of the island
11 """
12 # Get grid dimensions
13 rows, cols = len(grid), len(grid[0])
14 total_perimeter = 0
15
16 # Iterate through each cell in the grid
17 for row in range(rows):
18 for col in range(cols):
19 # Check if current cell is land
20 if grid[row][col] == 1:
21 # Each land cell contributes 4 sides initially
22 total_perimeter += 4
23
24 # Check if there's land below (subtract shared edge)
25 if row < rows - 1 and grid[row + 1][col] == 1:
26 # Subtract 2 (1 from current cell, 1 from neighbor)
27 total_perimeter -= 2
28
29 # Check if there's land to the right (subtract shared edge)
30 if col < cols - 1 and grid[row][col + 1] == 1:
31 # Subtract 2 (1 from current cell, 1 from neighbor)
32 total_perimeter -= 2
33
34 return total_perimeter
35
1class Solution {
2 public int islandPerimeter(int[][] grid) {
3 // Initialize perimeter counter
4 int perimeter = 0;
5
6 // Get grid dimensions
7 int rows = grid.length;
8 int cols = grid[0].length;
9
10 // Iterate through each cell in the grid
11 for (int row = 0; row < rows; row++) {
12 for (int col = 0; col < cols; col++) {
13 // Check if current cell is land (1)
14 if (grid[row][col] == 1) {
15 // Each land cell contributes 4 sides initially
16 perimeter += 4;
17
18 // Check if there's a land cell below (shared edge)
19 // If yes, subtract 2 (1 from current cell, 1 from neighbor)
20 if (row < rows - 1 && grid[row + 1][col] == 1) {
21 perimeter -= 2;
22 }
23
24 // Check if there's a land cell to the right (shared edge)
25 // If yes, subtract 2 (1 from current cell, 1 from neighbor)
26 if (col < cols - 1 && grid[row][col + 1] == 1) {
27 perimeter -= 2;
28 }
29 }
30 }
31 }
32
33 // Return the total perimeter of the island
34 return perimeter;
35 }
36}
37
1class Solution {
2public:
3 int islandPerimeter(vector<vector<int>>& grid) {
4 // Get grid dimensions
5 int rows = grid.size();
6 int cols = grid[0].size();
7
8 // Initialize perimeter counter
9 int perimeter = 0;
10
11 // Iterate through each cell in the grid
12 for (int row = 0; row < rows; ++row) {
13 for (int col = 0; col < cols; ++col) {
14 // Check if current cell is land (1)
15 if (grid[row][col] == 1) {
16 // Each land cell contributes 4 sides initially
17 perimeter += 4;
18
19 // Check if there's a land cell below (shared edge)
20 // If yes, subtract 2 (1 from current cell, 1 from neighbor)
21 if (row < rows - 1 && grid[row + 1][col] == 1) {
22 perimeter -= 2;
23 }
24
25 // Check if there's a land cell to the right (shared edge)
26 // If yes, subtract 2 (1 from current cell, 1 from neighbor)
27 if (col < cols - 1 && grid[row][col + 1] == 1) {
28 perimeter -= 2;
29 }
30 }
31 }
32 }
33
34 return perimeter;
35 }
36};
37
1/**
2 * Calculates the perimeter of an island in a grid
3 * @param grid - 2D array where 1 represents land and 0 represents water
4 * @returns The total perimeter of the island
5 */
6function islandPerimeter(grid: number[][]): number {
7 const rows: number = grid.length;
8 const cols: number = grid[0].length;
9 let perimeter: number = 0;
10
11 // Iterate through each cell in the grid
12 for (let row = 0; row < rows; row++) {
13 for (let col = 0; col < cols; col++) {
14 // Get the value of the cell above (0 if out of bounds)
15 let topCell: number = 0;
16 if (row > 0) {
17 topCell = grid[row - 1][col];
18 }
19
20 // Get the value of the cell to the left (0 if out of bounds)
21 let leftCell: number = 0;
22 if (col > 0) {
23 leftCell = grid[row][col - 1];
24 }
25
26 const currentCell: number = grid[row][col];
27
28 // Count edges between different cell types (land/water boundary)
29 if (currentCell !== topCell) {
30 perimeter++;
31 }
32 if (currentCell !== leftCell) {
33 perimeter++;
34 }
35 }
36 }
37
38 // Add perimeter contribution from the rightmost column
39 for (let row = 0; row < rows; row++) {
40 if (grid[row][cols - 1] === 1) {
41 perimeter++;
42 }
43 }
44
45 // Add perimeter contribution from the bottom row
46 for (let col = 0; col < cols; col++) {
47 if (grid[rows - 1][col] === 1) {
48 perimeter++;
49 }
50 }
51
52 return perimeter;
53}
54
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 algorithm uses nested loops to iterate through every cell in the grid exactly once. For each cell, it performs constant time operations (checking if it's land, adding to perimeter, and checking adjacent cells), resulting in O(1)
work per cell. Therefore, the total time complexity is O(m * n)
.
Space Complexity: O(1)
. The algorithm only uses a constant amount of extra space for variables (m
, n
, ans
, i
, j
) regardless of the input size. It modifies the perimeter count in-place without using any additional data structures that scale with the input size.
Common Pitfalls
1. Double-Counting Shared Edges
Pitfall: A common mistake is checking all four directions (up, down, left, right) for each land cell and subtracting 1 for each adjacent land cell found. This leads to double-counting because each shared edge gets processed twice - once from each of the two cells sharing it.
Example of Incorrect Approach:
# INCORRECT - counts each shared edge twice
for i in range(m):
for j in range(n):
if grid[i][j] == 1:
ans += 4
# Checking all 4 directions
if i > 0 and grid[i-1][j] == 1:
ans -= 1 # Up
if i < m-1 and grid[i+1][j] == 1:
ans -= 1 # Down
if j > 0 and grid[i][j-1] == 1:
ans -= 1 # Left
if j < n-1 and grid[i][j+1] == 1:
ans -= 1 # Right
Solution: Only check in two directions (right and down) and subtract 2 for each shared edge found. This ensures each edge is counted exactly once.
2. Incorrect Boundary Checking
Pitfall: Forgetting to check array bounds before accessing neighboring cells, leading to IndexError.
Example of Incorrect Code:
# INCORRECT - may cause IndexError if grid[i + 1][j] == 1: # Missing bounds check ans -= 2
Solution: Always verify that indices are within bounds before accessing array elements:
if i < m - 1 and grid[i + 1][j] == 1: ans -= 2
3. Wrong Subtraction Value
Pitfall: Subtracting only 1 instead of 2 when finding adjacent land cells. Remember that a shared edge affects the perimeter count of both cells sharing it.
Example of Incorrect Logic:
# INCORRECT - only subtracting 1 if i < m - 1 and grid[i + 1][j] == 1: ans -= 1 # Should be -2
Solution: Subtract 2 for each shared edge (1 from each cell's contribution).
4. Alternative Correct Approach
Instead of the add-4-then-subtract method, you can count exposed edges directly:
def islandPerimeter(self, grid: List[List[int]]) -> int:
rows, cols = len(grid), len(grid[0])
perimeter = 0
for i in range(rows):
for j in range(cols):
if grid[i][j] == 1:
# Check all 4 directions
# Top edge
if i == 0 or grid[i-1][j] == 0:
perimeter += 1
# Bottom edge
if i == rows-1 or grid[i+1][j] == 0:
perimeter += 1
# Left edge
if j == 0 or grid[i][j-1] == 0:
perimeter += 1
# Right edge
if j == cols-1 or grid[i][j+1] == 0:
perimeter += 1
return perimeter
This approach directly counts edges that face water or grid boundaries, avoiding the subtraction logic altogether.
Which of the following uses divide and conquer strategy?
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
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Want a Structured Path to Master System Design Too? Donโt Miss This!