DFS from Ocean Borders
Intuition
Instead of checking whether each cell can reach both oceans (which would be expensive), we reverse the problem: start from the ocean borders and flow uphill. We run DFS from every cell on the Pacific border (top row and left column) to find all cells reachable by the Pacific. Similarly, we run DFS from every cell on the Atlantic border (bottom row and right column). A cell where water can reach both oceans is one that appears in both reachable sets. This reversal dramatically reduces redundant work.
Algorithm
- Create two boolean matrices: `pacific` and `atlantic`, both initialized to false.
- Run DFS from every cell in the top row and left column, marking reachable cells in `pacific`. Water flows to cells with height greater than or equal to the current cell.
- Run DFS from every cell in the bottom row and right column, marking reachable cells in `atlantic`.
- Iterate through all cells. If a cell is marked true in both `pacific` and `atlantic`, add it to the result list.
- Return the result list.
O(m * n) where m and n are the dimensions of the gridSpaceO(m * n) for the two visited matrices and the recursion stackCode (C++ · Java · Python · Go)
class Solution {
public:
vector<vector<int>> pacificAtlantic(vector<vector<int>>& heights) {
int m = heights.size(), n = heights[0].size();
vector<vector<bool>> pacific(m, vector<bool>(n, false));
vector<vector<bool>> atlantic(m, vector<bool>(n, false));
for (int i = 0; i < m; i++) {
dfs(heights, pacific, i, 0);
dfs(heights, atlantic, i, n - 1);
}
for (int j = 0; j < n; j++) {
dfs(heights, pacific, 0, j);
dfs(heights, atlantic, m - 1, j);
}
vector<vector<int>> result;
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (pacific[i][j] && atlantic[i][j]) {
result.push_back({i, j});
}
}
}
return result;
}
private:
void dfs(vector<vector<int>>& heights, vector<vector<bool>>& visited, int r, int c) {
int m = heights.size(), n = heights[0].size();
visited[r][c] = true;
int dirs[4][2] = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
for (auto& d : dirs) {
int nr = r + d[0], nc = c + d[1];
if (nr >= 0 && nr < m && nc >= 0 && nc < n && !visited[nr][nc] && heights[nr][nc] >= heights[r][c]) {
dfs(heights, visited, nr, nc);
}
}
}
};class Solution {
private int[][] dirs = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
public List<List<Integer>> pacificAtlantic(int[][] heights) {
int m = heights.length, n = heights[0].length;
boolean[][] pacific = new boolean[m][n];
boolean[][] atlantic = new boolean[m][n];
for (int i = 0; i < m; i++) {
dfs(heights, pacific, i, 0);
dfs(heights, atlantic, i, n - 1);
}
for (int j = 0; j < n; j++) {
dfs(heights, pacific, 0, j);
dfs(heights, atlantic, m - 1, j);
}
List<List<Integer>> result = new ArrayList<>();
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (pacific[i][j] && atlantic[i][j]) {
result.add(List.of(i, j));
}
}
}
return result;
}
private void dfs(int[][] heights, boolean[][] visited, int r, int c) {
int m = heights.length, n = heights[0].length;
visited[r][c] = true;
for (int[] d : dirs) {
int nr = r + d[0], nc = c + d[1];
if (nr >= 0 && nr < m && nc >= 0 && nc < n && !visited[nr][nc] && heights[nr][nc] >= heights[r][c]) {
dfs(heights, visited, nr, nc);
}
}
}
}from typing import List
class Solution:
def pacificAtlantic(self, heights: List[List[int]]) -> List[List[int]]:
m, n = len(heights), len(heights[0])
pacific = set()
atlantic = set()
def dfs(r, c, visited):
visited.add((r, c))
for dr, dc in [(0, 1), (0, -1), (1, 0), (-1, 0)]:
nr, nc = r + dr, c + dc
if 0 <= nr < m and 0 <= nc < n and (nr, nc) not in visited and heights[nr][nc] >= heights[r][c]:
dfs(nr, nc, visited)
for i in range(m):
dfs(i, 0, pacific)
dfs(i, n - 1, atlantic)
for j in range(n):
dfs(0, j, pacific)
dfs(m - 1, j, atlantic)
return [[r, c] for r in range(m) for c in range(n) if (r, c) in pacific and (r, c) in atlantic]func pacificAtlantic(heights [][]int) [][]int {
m, n := len(heights), len(heights[0])
pacific := make([][]bool, m)
atlantic := make([][]bool, m)
for i := range pacific {
pacific[i] = make([]bool, n)
atlantic[i] = make([]bool, n)
}
dirs := [][2]int{{0, 1}, {0, -1}, {1, 0}, {-1, 0}}
var dfs func(visited [][]bool, r, c int)
dfs = func(visited [][]bool, r, c int) {
visited[r][c] = true
for _, d := range dirs {
nr, nc := r+d[0], c+d[1]
if nr >= 0 && nr < m && nc >= 0 && nc < n && !visited[nr][nc] && heights[nr][nc] >= heights[r][c] {
dfs(visited, nr, nc)
}
}
}
for i := 0; i < m; i++ {
dfs(pacific, i, 0)
dfs(atlantic, i, n-1)
}
for j := 0; j < n; j++ {
dfs(pacific, 0, j)
dfs(atlantic, m-1, j)
}
var result [][]int
for i := 0; i < m; i++ {
for j := 0; j < n; j++ {
if pacific[i][j] && atlantic[i][j] {
result = append(result, []int{i, j})
}
}
}
return result
}