The problem
There is 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] = [ri, ci]
denotes that rainwater can flow from cell (ri, ci)
to both the Pacific and Atlantic oceans.
Examples
Input: 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]]
Output: [[0,4],[1,3],[1,4],[2,2],[3,0],[3,1],[4,0]]
Explanation: The following cells can flow to the Pacific and Atlantic oceans, as shown below:
[0,4]: [0,4] -> Pacific Ocean
[0,4] -> Atlantic Ocean
[1,3]: [1,3] -> [0,3] -> Pacific Ocean
[1,3] -> [1,4] -> Atlantic Ocean
[1,4]: [1,4] -> [1,3] -> [0,3] -> Pacific Ocean
[1,4] -> Atlantic Ocean
[2,2]: [2,2] -> [1,2] -> [0,2] -> Pacific Ocean
[2,2] -> [2,3] -> [2,4] -> Atlantic Ocean
[3,0]: [3,0] -> Pacific Ocean
[3,0] -> [4,0] -> Atlantic Ocean
[3,1]: [3,1] -> [3,0] -> Pacific Ocean
[3,1] -> [4,1] -> Atlantic Ocean
[4,0]: [4,0] -> Pacific Ocean
[4,0] -> Atlantic Ocean
Note that there are other possible paths for these cells to flow to the Pacific and Atlantic oceans.
Input: heights = [[1]]
Output: [[0,0]]
Explanation: The water can flow from the only cell to the Pacific and Atlantic oceans.
Constraints
m == heights.length
n == heights[r].length
1 <= m, n <= 200
0 <= heights[r][c] <= 10^5
Depth-First Search (DFS) Solution
func pacificAtlantic(_ heights: [[Int]]) -> [[Int]] {
let ROWS = heights.count
let COLS = heights[0].count
var pacific: Set<[Int]> = []
var atlantic: Set<[Int]> = []
func dfs(
_ r: Int,
_ c: Int,
_ visit: inout Set<[Int]>,
_ prevHeight: Int
) {
if (visit.contains([r, c]) ||
r < 0 || c < 0 || r >= ROWS || c >= COLS ||
heights[r][c] < prevHeight
) {
return
}
visit.insert([r, c])
dfs(r + 1, c, &visit, heights[r][c])
dfs(r - 1, c, &visit, heights[r][c])
dfs(r, c + 1, &visit, heights[r][c])
dfs(r, c - 1, &visit, heights[r][c])
}
for c in 0 ..< COLS {
dfs(0, c, &pacific, heights[0][c])
dfs(ROWS - 1, c, &atlantic, heights[ROWS - 1][c])
}
for r in 0 ..< ROWS {
dfs(r, 0, &pacific, heights[r][0])
dfs(r, COLS - 1, &atlantic, heights[r, COLS - 1])
}
var res: [[Int]] = []
for r in 0 ..< ROWS {
for c in 0 ..< COLS {
if pacific.contains([r, c]) && atlantic.contains([r, c]) {
res.append([r, c])
}
}
}
return res
}
Explanation
From the description of the problem, we learned that we need to find the cells that can reach both the Atlantic and Pacific Oceans. This can happen only if an adjacent cell has a lower or equal value than the current cell. The water can flow in four directions (above, left, right, and below).
For example, with input:
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]]
5 > 3
,5 > 1
, and5 > 4
- The
3
to the right of5
has a neighbor of1
and follows the condition3 > 1
, meaning we can reach the Atlantic Ocean. - The
4
to the left of5
has a neighbor of2
, and also follows the condition4 > 2
, meaning we can reach the Pacific Ocean. - As a result, we include the position of
5
in our result.
One way to solve this problem is to find every cell that borders the Pacific Ocean (everything in the first row and first column). Similarly, we do the same for the Atlantic Ocean (everything in the last row and last column). We use graph traversal with DFS to find which positions can reach both oceans.
The first thing we do is iterate through the first row (Pacific) and run DFS to find all nodes that can reach the Pacific Ocean. We do the same for the bottom row (Atlantic). We maintain these values in a set to avoid duplicates.
Time/Space Complexity
- Time Complexity:
O(m * n)
- Space Complexity:
O(m * n)
- Where
m
is the number of rows, andn
is the number of columns.
Breadth-First Search (BFS) Solution
func pacificAtlantic(_ heights: [[Int]]) -> [[Int]] {
let ROWS = heights.count
let COLS = heights[0].count
let directions = [(1, 0), (-1, 0), (0, 1), (0, -1)]
var pac = Array(repeating: Array(repeating: false, count: COLS), count: ROWS)
var atl = Array(repeating: Array(repeating: false, count: COLS), count: ROWS)
func bfs(_ sources: [(Int, Int)], _ ocean: inout [[Bool]]) {
var q = sources
while !q.isEmpty {
let (r, c) = q.removeFirst()
ocean[r][c] = true
for (dr, dc) in directions {
let nr = r + dr
let nc = c + dc
if nr >= 0, nr < ROWS, nc >= 0, nc < COLS, !ocean[nr][nc], heights[nr][nc] >= heights[r][c] {
q.append((nr, nc))
}
}
}
}
}
Explanation
We can also solve this problem using BFS instead of DFS. The logic stays the same, but we use an iterative approach instead of recursion.
Time/Space Complexity remains O(m * n).