You are given a 2D grid represented by a matrix where 1
represents land and 0
represents water. An island is a group of connected 1
s surrounded by water. Two islands are considered the same if and only if one can be translated (shifted without rotation or reflection) to equal the other. Your task is to find the number of distinct islands.
For example:
Example 1:
Input:
grid = [
[1,1,0,0,0],
[1,1,0,0,0],
[0,0,0,1,1],
[0,0,0,1,1]
]
Output: 1
The two islands are considered the same because the second island is just a translation of the first.
Example 2:
Input:
grid = [
[1,1,0,1,1],
[1,0,0,0,0],
[0,0,0,0,1],
[1,1,0,1,1]
]
Output: 3
Explain how you would approach solving this problem efficiently. What data structures and algorithms would you use? How would you handle edge cases such as an empty grid, a grid with no islands, or islands touching only diagonally? Provide a code example illustrating your approach. What is the time and space complexity of your solution?
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:
We want to find the number of different island shapes in a grid. The brute force way is to explore each piece of land on the grid, recording the shape of the island it belongs to, and then comparing all the island shapes to see how many are truly distinct.
Here's how the algorithm would work step-by-step:
def number_of_distinct_islands(grid):
number_of_rows = len(grid)
number_of_columns = len(grid[0])
island_shapes = set()
def explore_island(row, column, path, start_row, start_column):
if row < 0 or row >= number_of_rows or column < 0 or column >= number_of_columns or grid[row][column] == 0:
return
grid[row][column] = 0
# Record the path relative to the starting point
path.append((row - start_row, column - start_column))
explore_island(row + 1, column, path, start_row, start_column)
explore_island(row - 1, column, path, start_row, start_column)
explore_island(row, column + 1, path, start_row, start_column)
explore_island(row, column - 1, path, start_row, start_column)
for row in range(number_of_rows):
for column in range(number_of_columns):
if grid[row][column] == 1:
island_path = []
# We need to capture the shape of this specific island.
explore_island(row, column, island_path, row, column)
island_shapes.add(tuple(island_path))
return len(island_shapes)
The goal is to identify unique island shapes within a grid. We can find all connected land pieces and represent the shape of each island as a unique string representing movements to explore it. We use this string as a way of 'fingerprinting' the island, and count how many unique fingerprints we find.
Here's how the algorithm would work step-by-step:
def number_of_distinct_islands(grid):
if not grid:
return 0
number_of_rows = len(grid)
number_of_columns = len(grid[0])
distinct_islands = set()
def explore_island(row, column, path):
if row < 0 or row >= number_of_rows or column < 0 or column >= number_of_columns or grid[row][column] == 0:
return
grid[row][column] = 0 # Mark as visited to avoid cycles.
path.append('D')
explore_island(row + 1, column, path)
path.append('U')
explore_island(row - 1, column, path)
path.append('R')
explore_island(row, column + 1, path)
path.append('L')
explore_island(row, column - 1, path)
path.append('B') #Backtrack
for row in range(number_of_rows):
for column in range(number_of_columns):
if grid[row][column] == 1:
# Found a new island, explore it.
island_path = []
explore_island(row, column, island_path)
# Generate a unique fingerprint for the island.
island_string = ''.join(island_path)
#Only add the island if it is unique
if island_string not in distinct_islands:
distinct_islands.add(island_string)
#The total count of the distinct islands are returned
return len(distinct_islands)
Case | How to Handle |
---|---|
Null or empty grid | Return 0 as there are no islands in an empty grid |
Grid with all water (all 0s) | Return 0 as there are no islands to count |
Grid with all land (all 1s) | Return 1 if all cells are connected forming only one island shape |
Grid with a single '1' surrounded by '0's | The algorithm correctly identifies this as a single island |
Large grid size, potential stack overflow with recursion | Use iterative DFS (using a stack data structure) instead of recursive DFS to prevent stack overflow errors |
Grid containing disconnected islands with identical shapes | The shape comparison logic will correctly identify these as duplicates. |
Grid with islands touching at corners only (not considered connected) | Ensure the DFS only considers adjacent (up, down, left, right) cells, not diagonal ones. |
Integer overflow in grid dimensions (rows * cols) | Check dimensions for reasonableness to avoid potential integer overflow when used in calculations |