You are given a binary matrix grid
of size m x n
. An island is a group of 1
s (representing land) connected 4-directionally (horizontal or vertical). You may assume all four edges of the grid are surrounded by water (represented by 0
s).
Two islands are considered the same if one can be transformed into the other by some combination of translation (shifting the island), rotation (90, 180, or 270 degrees), and reflection (flipping the island horizontally or vertically). Note that you can perform these transformations in any order and any number of times.
Write a function to find the number of distinct islands in grid
.
For example:
Example 1:
Input: grid = [
[1,1,0,0,0],
[1,0,0,0,0],
[0,0,0,0,1],
[0,0,0,1,1]
]
Output: 3
Explanation: There are three distinct islands. Note that the first two islands are considered different because they are not rotations or reflections of each other.
Example 2:
Input: grid = [
[1,1,0,0,0],
[1,1,0,0,0],
[0,0,0,1,1],
[0,0,0,1,1]
]
Output: 1
Explanation: The two islands are the same because one can be rotated 180 degrees to match the other.
Constraints:
m == grid.length
n == grid[i].length
1 <= m, n <= 50
grid[i][j]
is either 0
or 1
.When you get asked this question in a real-life environment, it will often be ambiguous (especially at FAANG). Make sure to ask these questions in that case:
The core idea is to find all unique island shapes, treating rotated and reflected versions as the same. The brute force method explores every possible island by examining the entire grid for connected land masses.
Here's how the algorithm would work step-by-step:
def number_of_distinct_islands_brute_force(grid):
rows = len(grid)
cols = len(grid[0])
islands = set()
def explore_island(row, col, path, start_row, start_col):
if row < 0 or row >= rows or col < 0 or col >= cols or grid[row][col] == 0:
return
grid[row][col] = 0 # Mark as visited
path.append((row - start_row, col - start_col))
explore_island(row + 1, col, path, start_row, start_col)
explore_island(row - 1, col, path, start_row, start_col)
explore_island(row, col + 1, path, start_row, start_col)
explore_island(row, col - 1, path, start_row, start_col)
def normalize_island_shape(island_shape):
normalized_shape = tuple(sorted(island_shape))
return normalized_shape
def generate_rotations_and_reflections(island_shape):
rotated_and_reflected_shapes = set()
for _ in range(4):
# Rotate the island 90 degrees
rotated_shape = [(y, -x) for x, y in island_shape]
rotated_and_reflected_shapes.add(normalize_island_shape(rotated_shape))
# Reflect the island along the x-axis
reflected_shape = [(-x, y) for x, y in rotated_shape]
rotated_and_reflected_shapes.add(normalize_island_shape(reflected_shape))
island_shape = rotated_shape # Update for next rotation
return rotated_and_reflected_shapes
for row in range(rows):
for col in range(cols):
if grid[row][col] == 1:
island_path = []
# Find island and mark all visited lands
explore_island(row, col, island_path, row, col)
# Generate all rotations and reflections
all_shapes = generate_rotations_and_reflections(island_path)
is_unique = True
for shape in all_shapes:
if shape in islands:
#If the shape is found skip to next landmass
is_unique = False
break
if is_unique:
#if unique store shape to avoid duplicates
islands.add(normalize_island_shape(island_path))
return len(islands)
The goal is to identify distinct island shapes, even if they are rotated or flipped. The key is to transform each island into a unique, comparable representation by exploring its structure and normalizing it.
Here's how the algorithm would work step-by-step:
def number_of_distinct_islands_two(grid):
number_of_rows = len(grid)
number_of_columns = len(grid[0]) if number_of_rows > 0 else 0
visited = set()
distinct_islands = set()
def explore_island(row, column, current_island_cells):
if (row < 0 or row >= number_of_rows or column < 0 or
column >= number_of_columns or grid[row][column] == 0 or
(row, column) in visited):
return
visited.add((row, column))
current_island_cells.append((row, column))
explore_island(row + 1, column, current_island_cells)
explore_island(row - 1, column, current_island_cells)
explore_island(row, column + 1, current_island_cells)
explore_island(row, column - 1, current_island_cells)
def normalize_island(island_cells):
min_row = min(row for row, column in island_cells)
min_column = min(column for row, column in island_cells)
return [(row - min_row, column - min_column) for row, column in island_cells]
def rotate_island(island_cells):
return [(-column, row) for row, column in island_cells]
def flip_island(island_cells):
return [(row, -column) for row, column in island_cells]
def get_island_string(island_cells):
return str(sorted(island_cells))
def get_unique_island_string(island_cells):
unique_strings = set()
for _ in range(4):
unique_strings.add(get_island_string(normalize_island(island_cells)))
unique_strings.add(get_island_string(normalize_island(flip_island(island_cells))))
island_cells = rotate_island(island_cells)
return min(unique_strings)
for row in range(number_of_rows):
for column in range(number_of_columns):
if grid[row][column] == 1 and (row, column) not in visited:
current_island_cells = []
explore_island(row, column, current_island_cells)
# Generating unique string representation
unique_island_string = get_unique_island_string(current_island_cells)
distinct_islands.add(unique_island_string)
# Return the number of distinct island shapes.
return len(distinct_islands)
Case | How to Handle |
---|---|
Null or empty grid | Return an empty list or appropriate error code indicating no islands if the input grid is null or empty. |
Grid with all zeros (no land) | The algorithm should return an empty list because no islands exist. |
Grid with only one cell as land (a single island) | The algorithm should correctly identify this single-cell island and its eight transformations. |
Grid with a very large island, potentially causing stack overflow if recursion is used for DFS/BFS | Consider using an iterative approach for DFS/BFS to avoid stack overflow issues on very large islands. |
Two islands are identical after rotation/reflection, but are initially at different locations | The solution must ensure that the normalized representations are identical to correctly identify them as the same island. |
Integer overflow during coordinate transformations or centroid calculations | Use appropriate data types (e.g., long) to store coordinate values and calculations to prevent overflow. |
Maximum grid size causing memory issues when storing island shapes or normalized forms | Optimize memory usage by efficiently representing island shapes (e.g., using relative coordinates) and avoid storing unnecessary data. |
Island shapes that are symmetrical, potentially leading to duplicate normalized forms | The normalization process should generate a unique representation, even for symmetrical islands. |