Pacific Atlantic Water Flow looks like a maze problem, but its real lesson is a classic interview move: when "from every cell, can I reach the goal?" is expensive, flip the search and start from the goal instead.
Problem. You are given an m x n grid of heights. The Pacific ocean touches the top edge and
left edge; the Atlantic touches the bottom edge and right edge. Water flows from a cell to a
neighbor (up/down/left/right) only if that neighbor is equal or lower in height. Return every cell
from which water can flow to both oceans.
Example grid:
3 3 3
3 1 4
2 4 5Every cell can reach both oceans except the center 1 — it sits in a pit lower than all its
neighbors, so water can neither climb out of it nor flow into it.
The slow way first
The literal reading: for each of the m × n cells, run a flood-fill downhill and see whether it can dribble all the way to a Pacific edge, then do it again for the Atlantic. That is a full grid traversal per starting cell — roughly O((m·n)²). Far too slow.
The question to ask: what am I re-computing? The same downhill paths, over and over, from different starts. There is a much cheaper framing hiding here.
The idea: search uphill from each ocean
Reverse the flow. If water can run downhill from cell A to the ocean, then standing at the ocean we can walk uphill back to A. So start at the ocean border and do one flood-fill that climbs to neighbors of equal or greater height. Every cell that fill touches can drain to that ocean.
Run that flood-fill once per ocean — seeding from the Pacific border, then from the Atlantic border — and you get two reachable sets. The answer is their intersection.
The key insight: searching from the oceans visits each cell a constant number of times total, turning O((m·n)²) into O(m·n).
Walk through it
Step through the animation. First the Pacific search seeds the top row and left column, then climbs uphill — reaching everything except the center pit. The Atlantic search does the same from the opposite corner. Overlay the two sets and every cell except the lone 1 is reachable from both.
Pseudocode
make two empty sets: pacific, atlantic
define climb(r, c, seen, prev):
if (r,c) already in seen, or this cell is LOWER than prev: stop
add (r,c) to seen
for each of the 4 neighbors in bounds:
climb(neighbor, seen, height of this cell) # uphill: prev = my height
seed climb from every Pacific border cell into "pacific"
seed climb from every Atlantic border cell into "atlantic"
return every cell that is in BOTH setsThe Python solution
def pacific_atlantic(heights):
rows, cols = len(heights), len(heights[0])
pac, atl = set(), set()
def dfs(r, c, seen, prev):
if (r, c) in seen or heights[r][c] < prev:
return
seen.add((r, c))
for dr, dc in ((1,0),(-1,0),(0,1),(0,-1)):
if 0 <= r+dr < rows and 0 <= c+dc < cols:
dfs(r+dr, c+dc, seen, heights[r][c])
for c in range(cols):
dfs(0, c, pac, 0); dfs(rows-1, c, atl, 0)
for r in range(rows):
dfs(r, 0, pac, 0); dfs(r, cols-1, atl, 0)
return [list(p) for p in pac & atl]pacandatlare the sets of coordinates reachable from each ocean.dfsclimbs uphill: it stops if it has seen the cell, or if the cell is lower thanprev(the height it just came from) — that is the reversed flow rule.- When we recurse we pass
heights[r][c]as the newprev, so the next cell must be at least this tall. - The two loops seed the search from every border cell: top/bottom rows for the columns, left/right columns for the rows.
pac & atlis Python set intersection — exactly the cells that drain to both oceans.
Complexity
| Case | Time | Notes |
|---|---|---|
| Brute force (flood per cell) | O((m·n)²) (moderate) | re-walk the grid from every start |
| Search from oceans (this) | O(m·n) (moderate) | each cell visited a constant number of times |
O(m·n) (moderate)We visit each cell a bounded number of times across both floods, so the work is linear in the grid size. The space is the two visited sets plus the recursion stack, all O(m·n).
When this pattern shows up
When a problem asks "from how many starts can I reach the goal," check whether searching backward from the goal collapses many searches into one. Multi-source BFS/DFS from a set of seeds — fire spreading from several cells, distance to the nearest exit, rotting oranges — is the same move.
Mind the direction. Because we search from the ocean, the rule flips: we move to neighbors that are equal or higher, not lower. Comparing against the wrong direction (or forgetting the equal case) silently drops valid cells.
Practice
In the 3x3 example, why is the center cell (height 1) the only one that reaches neither ocean?
1. Why do we search starting from the ocean borders instead of from each cell?
2. When searching FROM an ocean, which neighbors do we move to?
3. How is the final answer computed from the two reachable sets?
4. What is the time complexity of the ocean-seeded solution?