You are given an m x n
rectangular island that borders both the Pacific Ocean and Atlantic Ocean. The Pacific Ocean touches the island's left and top edges, and the Atlantic Ocean touches the island's right and bottom edges.
The island is partitioned into a grid of square cells. You are given an m x n
integer matrix heights
where heights[r][c]
represents the height above sea level of the cell at coordinate (r, c)
.
The island receives a lot of rain, and the rainwater can flow to neighboring cells directly north, south, east, and west if the neighboring cell's height is less than or equal to the current cell's height. Water can flow from any cell adjacent to an ocean into the ocean.
Return a 2D list of grid coordinates result
where result[i] = [r<sub>i</sub>, c<sub>i</sub>]
denotes that rainwater can flow from cell (r<sub>i</sub>, c<sub>i</sub>)
to both the Pacific and Atlantic oceans.
Example:
Consider the following heights
matrix:
heights = [
[1,2,2,3,5],
[3,2,3,4,4],
[2,4,5,3,1],
[6,7,1,4,5],
[5,1,1,2,4]
]
In this case, the expected output would be:
[[0,4],[1,3],[1,4],[2,2],[3,0],[3,1],[4,0]]
Explain how to efficiently determine the cells from which water can flow to both the Pacific and Atlantic oceans. What is the time and space complexity of your solution? Also, explain how your code handles edge cases, such as an empty matrix or a single-cell matrix.
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 problem asks us to find which locations in a grid can flow to both the Pacific and Atlantic oceans. A brute force approach involves checking every single location to see if it can reach both oceans.
Here's how the algorithm would work step-by-step:
def pacific_atlantic(heights):
if not heights:
return []
number_of_rows = len(heights)
number_of_columns = len(heights[0])
reachable_pacific = set()
reachable_atlantic = set()
result = []
def explore(row, column, reachable_set):
# Add current cell to reachable set.
reachable_set.add((row, column))
directions = [(0, 1), (0, -1), (1, 0), (-1, 0)]
for row_change, column_change in directions:
new_row = row + row_change
new_column = column + column_change
# Check if valid neighbor
if 0 <= new_row < number_of_rows and 0 <= new_column < number_of_columns \
and (new_row, new_column) not in reachable_set \
and heights[new_row][new_column] >= heights[row][column]:
explore(new_row, new_column, reachable_set)
# Explore from Pacific edge
for row in range(number_of_rows):
explore(row, 0, reachable_pacific)
for column in range(number_of_columns):
explore(0, column, reachable_pacific)
# Explore from Atlantic edge
for row in range(number_of_rows):
explore(row, number_of_columns - 1, reachable_atlantic)
for column in range(number_of_columns):
explore(number_of_rows - 1, column, reachable_atlantic)
# Find cells reachable from both
for row in range(number_of_rows):
for column in range(number_of_columns):
if (row, column) in reachable_pacific and (row, column) in reachable_atlantic:
result.append([row, column])
return result
The key idea is to start from the oceans and work our way inland. We use the property that water can only flow from higher or equal elevation to lower elevation to determine what cells can reach each ocean.
Here's how the algorithm would work step-by-step:
def pacific_atlantic(heights):
if not heights:
return []
number_of_rows = len(heights)
number_of_columns = len(heights[0])
pacific_reachable = [[False] * number_of_columns for _ in range(number_of_rows)]
atlantic_reachable = [[False] * number_of_columns for _ in range(number_of_rows)]
def depth_first_search(row, column, reachable_matrix, previous_height):
if row < 0 or row >= number_of_rows or column < 0 or column >= number_of_columns or \
reachable_matrix[row][column] or heights[row][column] < previous_height:
return
reachable_matrix[row][column] = True
# Explore adjacent cells with higher or equal height.
depth_first_search(row + 1, column, reachable_matrix, heights[row][column])
depth_first_search(row - 1, column, reachable_matrix, heights[row][column])
depth_first_search(row, column + 1, reachable_matrix, heights[row][column])
depth_first_search(row, column - 1, reachable_matrix, heights[row][column])
# Start DFS from all border cells connected to the Pacific.
for column_index in range(number_of_columns):
depth_first_search(0, column_index, pacific_reachable, -1)
for row_index in range(number_of_rows):
depth_first_search(row_index, 0, pacific_reachable, -1)
# Start DFS from all border cells connected to the Atlantic.
for column_index in range(number_of_columns):
depth_first_search(number_of_rows - 1, column_index, atlantic_reachable, -1)
for row_index in range(number_of_rows):
depth_first_search(row_index, number_of_columns - 1, atlantic_reachable, -1)
result = []
# Cells reachable from both oceans are added to the result.
for row_index in range(number_of_rows):
for column_index in range(number_of_columns):
if pacific_reachable[row_index][column_index] and atlantic_reachable[row_index][column_index]:
result.append([row_index, column_index])
return result
Case | How to Handle |
---|---|
Null or empty matrix | Return an empty list if the input matrix is null or has zero rows/columns to avoid null pointer exceptions and invalid calculations. |
Matrix with a single row or column | The solution should correctly identify cells that flow to both oceans even with only one row or column. |
Matrix with all identical values | All cells should be reachable from both oceans, leading to a result containing all cells. |
Large matrix (potential stack overflow with recursion) | Use iterative depth-first search (DFS) or breadth-first search (BFS) instead of recursive DFS to avoid stack overflow errors for large inputs. |
Matrix with integer overflow potential during calculations | The algorithm doesn't perform any arithmetic operations that could lead to an overflow, so this is not a concern. |
Matrix with negative height values (invalid input) | Treat negative height values as an error or assume all values are non-negative based on problem constraints. |
A matrix where no cell can reach both oceans | The algorithm should correctly return an empty list when no cell satisfies the condition. |
Connectivity cycles that could cause infinite loops | The visited set prevents cycles during DFS/BFS by marking cells that are currently being explored. |