You are given an m x n
integer matrix heightMap
representing the height of each unit cell in a 2D elevation map. Your task is to implement an algorithm to calculate the volume of water it can trap after raining.
Example 1:
Consider the following heightMap
:
[ [1, 4, 3, 1, 3, 2],
[3, 2, 1, 3, 2, 4],
[2, 3, 3, 2, 3, 1] ]
In this example, the algorithm should return 4
because water is trapped in two small ponds of sizes 1 and 3.
Example 2:
Consider the following heightMap
:
[ [3, 3, 3, 3, 3],
[3, 2, 2, 2, 3],
[3, 2, 1, 2, 3],
[3, 2, 2, 2, 3],
[3, 3, 3, 3, 3] ]
In this example, the algorithm should return 10
.
Clarifications:
heightMap
is invalid (e.g., null
or empty)?heightMap
and the values of the heights?Can you provide an efficient solution for this problem, considering both time and space complexity?
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 finding trapped rain water involves checking every single possible location to see how much water it can hold. We consider each spot as a potential water-holding location and see what the water level would be. We then add up all of the water held at each location to get the total trapped water.
Here's how the algorithm would work step-by-step:
def trapping_rain_water_ii_brute_force(heights):
if not heights or not heights[0]:
return 0
rows = len(heights)
cols = len(heights[0])
total_water = 0
for row_index in range(rows):
for col_index in range(cols):
# Iterate through each cell to calculate trapped water.
left_max_height = 0
for k in range(col_index):
left_max_height = max(left_max_height, heights[row_index][k])
right_max_height = 0
for k in range(col_index + 1, cols):
right_max_height = max(right_max_height, heights[row_index][k])
top_max_height = 0
for k in range(row_index):
top_max_height = max(top_max_height, heights[k][col_index])
bottom_max_height = 0
for k in range(row_index + 1, rows):
bottom_max_height = max(bottom_max_height, heights[k][col_index])
# Determine the maximum water level.
max_water_level = min(left_max_height, right_max_height,\
top_max_height, bottom_max_height)
# Check if current cell can hold water.
if max_water_level > heights[row_index][col_index]:
# Calculate water held and add to the total.
total_water += max_water_level - heights[row_index][col_index]
return total_water
The key idea is to think of the water level rising gradually. We focus on the outer edges and slowly work our way inwards, filling the low spots with water along the way. The lowest boundary always determines how much water can be trapped.
Here's how the algorithm would work step-by-step:
import heapq
def trapping_rain_water_ii(heights):
if not heights or not heights[0]:
return 0
rows, columns = len(heights), len(heights[0])
visited = [[False] * columns for _ in range(rows)]
priority_queue = []
# Add all boundary cells to the priority queue.
for row_index in range(rows):
for column_index in range(columns):
if row_index == 0 or row_index == rows - 1 or column_index == 0 or column_index == columns - 1:
heapq.heappush(priority_queue, (heights[row_index][column_index], row_index, column_index))
visited[row_index][column_index] = True
total_water = 0
directions = [(0, 1), (0, -1), (1, 0), (-1, 0)]
while priority_queue:
current_height, current_row, current_column = heapq.heappop(priority_queue)
# Explore each neighbor.
for direction_row, direction_column in directions:
new_row = current_row + direction_row
new_column = current_column + direction_column
if 0 <= new_row < rows and 0 <= new_column < columns and not visited[new_row][new_column]:
# Calculate trapped water if neighbor is lower.
total_water += max(0, current_height - heights[new_row][new_column])
# The height of the new cell is the maximum of its original
# height and the current height, ensuring water is kept.
heapq.heappush(priority_queue, (max(heights[new_row][new_column], current_height), new_row, new_column))
visited[new_row][new_column] = True
return total_water
Case | How to Handle |
---|---|
Null or empty height map | Return 0 immediately as no water can be trapped in an empty or null map. |
Height map with dimensions 1xN or Nx1 | Return 0 immediately because at least two rows and two columns are needed to trap water. |
Height map with all cells having the same height | Return 0 as there's no height difference to trap any water. |
Height map with very large dimensions (e.g., 1000x1000) | Ensure the priority queue and visited set do not cause memory issues and that the algorithm's time complexity remains efficient (O(M*N*log(M+N)) when M and N are dimensions). |
Height map with extremely large height values that could cause integer overflow during calculations. | Use long data type to store height values and water levels to avoid integer overflow. |
Height map with negative height values | Treat negative height values as valid input and the algorithm should still compute trapped water correctly as it relies on relative height differences. |
Height map where the boundary is lower than the internal cells. | The algorithm correctly uses the boundary cells as the starting point for the priority queue and propagates inward. |
Height map contains zero height cells scattered throughout the map. | The algorithm should correctly handle zero-height cells as potential water-holding areas or paths for water to escape. |