You are given an empty n x m
grid initially filled with water. You are going to perform an add land operation. For each operation, you will be given a position pos = (row, col)
which represents the location where you will add land. By definition, a piece of land is surrounded by water and is formed by connecting adjacent lands horizontally or vertically. You may assume all four edges of the grid are all surrounded by water.
Return an array of integers that represents the number of islands after each add land operation.
Example 1:
Input: m = 3, n = 3, positions = [[0,0],[0,1],[1,2],[2,1]] Output: [1,1,2,3]
Example 2:
Input: m = 1, n = 1, positions = [[0,0]] Output: [1]
Constraints:
1 <= m, n <= 300
1 <= positions.length <= m * n
positions[i]
are unique.positions[i].length == 2
0 <= positions[i][0] < m
0 <= positions[i][1] < n
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 brute force approach to the 'Number of Islands II' problem involves checking the number of islands after each addition of land. We treat each land addition as a separate and independent scenario. This means we re-evaluate the entire grid to count the islands every time we add a new piece of land.
Here's how the algorithm would work step-by-step:
def number_of_islands_ii_brute_force(number_of_rows, number_of_columns, positions):
island_counts = []
grid = [[0] * number_of_columns for _ in range(number_of_rows)]
for position in positions:
row, column = position
# Mark the current position as land
grid[row][column] = 1
number_of_islands = 0
visited = set()
# Iterate through the entire grid to count islands
for row_index in range(number_of_rows):
for column_index in range(number_of_columns):
if grid[row_index][column_index] == 1 and (row_index, column_index) not in visited:
number_of_islands += 1
depth_first_search(grid, row_index, column_index, visited, number_of_rows, number_of_columns)
island_counts.append(number_of_islands)
return island_counts
def depth_first_search(grid, row, column, visited, number_of_rows, number_of_columns):
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))
# Explore adjacent land cells to find the entire island.
depth_first_search(grid, row + 1, column, visited, number_of_rows, number_of_columns)
depth_first_search(grid, row - 1, column, visited, number_of_rows, number_of_columns)
depth_first_search(grid, row, column + 1, visited, number_of_rows, number_of_columns)
depth_first_search(grid, row, column - 1, visited, number_of_rows, number_of_columns)
This problem involves tracking islands as we add land cells to a sea. The key idea is to use a structure that efficiently determines if newly added land connects existing islands and to merge those islands into one.
Here's how the algorithm would work step-by-step:
class Solution:
def numIslands2(self, grid_rows, grid_cols, positions):
parent = [i for i in range(grid_rows * grid_cols)]
rank = [0] * (grid_rows * grid_cols)
island_count = 0
result = []
def find(node_id):
if parent[node_id] != node_id:
parent[node_id] = find(parent[node_id])
return parent[node_id]
def union(node_id_one, node_id_two):
root_one = find(node_id_one)
root_two = find(node_id_two)
if root_one != root_two:
# Attach smaller rank tree under root of high rank tree
if rank[root_one] > rank[root_two]:
parent[root_two] = root_one
elif rank[root_one] < rank[root_two]:
parent[root_one] = root_two
# If ranks are same, then make one root and increment
else:
parent[root_two] = root_one
rank[root_one] += 1
return True
return False
grid = [[0] * grid_cols for _ in range(grid_rows)]
for row, col in positions:
if grid[row][col] == 1:
result.append(island_count)
continue
grid[row][col] = 1
island_count += 1
current_node_id = row * grid_cols + col
neighbors = [(row - 1, col), (row + 1, col), (row, col - 1), (row, col + 1)]
for neighbor_row, neighbor_col in neighbors:
if 0 <= neighbor_row < grid_rows and 0 <= neighbor_col < grid_cols and grid[neighbor_row][neighbor_col] == 1:
neighbor_node_id = neighbor_row * grid_cols + neighbor_col
# Merging islands and decrementing count
if union(current_node_id, neighbor_node_id):
island_count -= 1
result.append(island_count)
return result
Case | How to Handle |
---|---|
Empty grid dimensions (m=0 or n=0) | Return an empty list because no islands can be formed in an empty grid. |
Null or empty positions array | Return an empty list if the positions array is null or empty, as no islands are being added. |
Positions outside the grid boundaries | Ignore positions that are out of bounds to prevent array index out of bounds errors. |
Duplicate positions in the positions array | The union-find data structure will correctly merge islands even if the same position is added multiple times. |
Large grid dimensions (m and n are very large) | The union-find data structure should be optimized for path compression and union by rank to handle large grids efficiently. |
Integer overflow when calculating the position index | Use long data type to store the index to prevent overflow when calculating the flattened index (row * n + col). |
Maximum number of operations | Ensure union-find data structure operations do not exceed time limits, and optimize for path compression and union by rank. |
Grid already full of land before any add operations | The algorithm should correctly count islands that were initially connected and update the count as new islands connect with existing ones. |