Water flows from any cell to a neighbor of equal or lesser height, reaching the Pacific (top/left edges) or Atlantic (bottom/right edges). The trick: reverse the flow — DFS uphillfrom each ocean's border, then collect cells the two reachable sets share.
Given an R×C grid of heights, water at any cell can flow to an adjacent cell (up/down/left/right) of equal or lesser height. The Pacific Ocean borders the top and left edges; the Atlantic Ocean borders the bottom and right edges. Return all cells from which water can reach both oceans.
Example: for the 5×5 grid [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], the answer is [0,4],[1,3],[1,4],[2,2],[3,0],[3,1],[4,0].
≥ current. Do this once for each ocean, then intersect the two reachable sets.pacific and atlantic) — one per ocean.≥ current height (reverse flow). Mark them in pacific.atlantic.[r,c] present in both sets — these are the answer.neighbor.height ≥ current.height (uphill, because flow is reversed), not neighbor.height ≤ current.height. Getting it backwards yields the wrong reachable set.Space is also O(R·C) for the two visited sets and the implicit DFS call stack (worst case: the whole grid is reachable). You can replace the recursive DFS with an iterative BFS queue for the same complexity and no stack-overflow risk on large grids.
function pacificAtlantic(heights: number[][]): number[][] {
const R = heights.length, C = heights[0].length;
const pacific = new Set<number>();
const atlantic = new Set<number>();
const idx = (r: number, c: number) => r * C + c;
function dfs(r: number, c: number, reach: Set<number>): void {
if (reach.has(idx(r, c))) return;
reach.add(idx(r, c));
const dirs: [number, number][] = [[-1,0],[1,0],[0,-1],[0,1]];
for (const [dr, dc] of dirs) {
const nr = r + dr, nc = c + dc;
if (nr < 0 || nr >= R || nc < 0 || nc >= C) continue;
if (reach.has(idx(nr, nc))) continue;
if (heights[nr][nc] >= heights[r][c]) dfs(nr, nc, reach);
}
}
// Seed Pacific: top row + left column
for (let c = 0; c < C; c++) dfs(0, c, pacific);
for (let r = 1; r < R; r++) dfs(r, 0, pacific);
// Seed Atlantic: bottom row + right column
for (let c = 0; c < C; c++) dfs(R - 1, c, atlantic);
for (let r = 0; r < R - 1; r++) dfs(r, C - 1, atlantic);
// Intersection
const result: number[][] = [];
for (let r = 0; r < R; r++)
for (let c = 0; c < C; c++)
if (pacific.has(idx(r, c)) && atlantic.has(idx(r, c)))
result.push([r, c]);
return result;
}Set<number> objects keyed by r * C + c. The helper idx converts (row,col) → a unique integer, avoiding nested arrays.reach set as a parameter so the same function serves both oceans. It marks the current cell visited, then recurses into any in-bounds, not-yet-visited neighbor whose height is ≥ heights[r][c] — the reversal of normal downhill flow.r=0) and left column (c=0) is a border cell that trivially reaches the Pacific. We call dfs on each; the visited-check at line 8 prevents revisiting corners.r=R-1) and right column (c=C-1) seed the Atlantic pass identically.dfs call with a queue seeded from the same border cells; enqueue each newly marked cell. Same O(R·C) complexity but avoids call-stack overflow on large grids.≥ to > in the neighbor condition. The algorithm structure stays identical.result.length. The real follow-up is whether you could count without materializing the result array (yes, just increment a counter).| water flow / reachability on a height grid | reverse DFS uphill from border |
| two sources, need cells reachable by both | two BFS/DFS sets + intersect |
| "cells connected to the border" (Surrounded Regions, Enclaves) | multi-source BFS from all border cells at once |
| naïve per-cell search is O(n²) | flip direction: seed from targets, single shared traversal |
function pacificAtlantic(heights: number[][]): number[][] {
const R = heights.length, C = heights[0].length;
const pacific = new Set<number>(), atlantic = new Set<number>();
const idx = (r: number, c: number) => r * C + c;
function dfs(r: number, c: number, reach: Set<number>): void {
if (reach.has(idx(r, c))) return;
reach.add(idx(r, c));
// explore 4-directional neighbors that are >= heights[r][c]
}
// seed pacific: top row + left col
// seed atlantic: bottom row + right col
// return intersection
}height ≥ current is equivalent to asking which cells can drain down into the ocean.5, the answer is:[0,4] (height = 5, top-right corner) is in the answer because: