Lesson 44 — Graphs IV: search backwards

~25 minutes · Day 55 · reverse flood fill · Python

Tonight's problem is Pacific Atlantic Water Flow. It is a classic interview trap because the obvious direction feels natural: start at every cell and ask where the water can drain. That works on tiny grids, then collapses when the interviewer expects you to notice the question is easier backwards.

The invariant

Instead of asking where each cell drains, flood inward from each ocean and intersect the two reachable sets.

Pacific Atlantic: the slow way first

The grid cells are the graph nodes. Two cells have an edge if they are up, down, left, or right neighbors. Water can flow from a cell to a neighbor with height less than or equal to it. A plain graph traversal can follow those edges.

Brute force is: for every cell, run a downhill search and see whether that one search touches the Pacific border and the Atlantic border. If the grid is R * C cells, one search can touch R * C cells, and you might do that from every cell: O((R*C)^2) time.

Pacific Atlantic: search backwards

Reverse the question. Instead of starting from a high cell and flowing down, start from an ocean edge and walk inward to cells that could have flowed down into you. That means the comparison flips: from the ocean side, you may move only to an equal-or-higher neighbor.

Python's set is the right memory structure here: one set for cells that can reach the Pacific, one set for cells that can reach the Atlantic, then a set intersection for cells that can reach both.

def flood(starts):
    seen, stack = set(starts), list(starts)       # cells reaching one ocean
    while stack:
        r, c = stack.pop()
        for nr, nc in neighbors(r, c):            # in-bounds four-neighbors
            if (nr, nc) in seen: continue
            if heights[nr][nc] < heights[r][c]: continue
            seen.add((nr, nc)); stack.append((nr, nc))  # climb inward
    return seen

pacific = flood(pacific_border)                  # top row + left col
atlantic = flood(atlantic_border)                # bottom row + right col
answer = list(pacific & atlantic)                # both oceans

In the real submission, neighbors just yields the four in-bounds neighbors, and the two border lists are built from the outer rows and columns. The invariant is doing the heavy lifting: two floods, then intersect.

Pacific border: top row + left column Atlantic border: bottom row + right column 3 1 2 1 5 1 2 1 3 reaches Pacific only reaches Atlantic only reaches both — answer

Numbers are heights. Each flood climbs inward from its own border to equal-or-higher cells; the three dotted cells lie in both reachable sets, so they drain to both oceans.

Transfer: same question, new costume

New problem: a city grid has two emergency exits, one along the north-west border and one along the south-east border. A signal can travel from a high tower to an equal-or-lower neighboring tower. Which towers can reach both exits?

Before opening the reveal, predict the direction: do you start from every tower, or from the exits?

Your prediction first — then read on.

Start from the exits. From each exit border, flood inward only to equal-or-higher towers, then intersect the two reachable sets. Same invariant, different story.

What the trade buys you

ApproachTimeSpace
Brute force: search downhill from every cellO((R*C)^2)O(R*C)
Reverse flooding: search inward from both oceansO(R*C)O(R*C)

The improvement comes from visiting each cell at most once per ocean, instead of rediscovering the same downhill regions from many different starting cells.

Practice — answer before you scroll past

1. Which direction makes Pacific Atlantic efficient?

2. During a reverse flood, which neighbor may you enter?

3. Spot the bug in this reverse flood condition:

if heights[nr][nc] <= heights[r][c]:
    seen.add((nr, nc))

4. After both ocean floods, what is returned?

Your move (tonight)

Solve Pacific Atlantic Water Flow in your Notion tracker using the cycle: predict → attempt → compare → encode.

  1. Predict: say the brute force and the reverse-flood invariant out loud.
  2. Attempt: code the two floods without watching a solution first.
  3. Compare: check where your comparison direction or visited sets differed.
  4. Encode: write the invariant in your own words in Notion.
One primary source

Watch NeetCode — Pacific Atlantic Water Flow after your attempt, as the compare step. Focus on the moment he flips the search direction.