Pattern 3: DFS on a grid (flood fill)
Here the grid reveals its graph nature: neighboring cells are "connected". To explore an entire connected region, depth-first search (DFS) "floods" out from a cell, spreading to the neighbors — like the paint bucket of an image editor.
Try it first: Number of Islands
Before reading the theory, try to solve it. You can start with brute force, you can fail — the point is to feel the problem. The theory comes right below.
Given a grid of 1 (land) and 0 (water), count the number of islands. An island is a group of 1s connected horizontally/vertically.
Example: [[1,1,0,0],[1,0,0,1],[0,0,1,1]] → 2.
Tried it? Maybe you tried counting 1s or finding borders — and got lost in the cases. The theory below gives the right framework: each new island triggers a flood that marks all the connected land.
The theory: flood fill with DFS
Think of "filling with the paint bucket": from a cell, you spread to the neighbors of the same region, and from those neighbors to their neighbors, until the region runs out. That is DFS on a grid:
dfs(r, c):
if (r,c) is outside the grid → stop
if (r,c) is not in the region,
or was already visited → stop
mark (r,c) as visited
dfs(r+1, c); dfs(r-1, c) # the 4 neighbors
dfs(r, c+1); dfs(r, c-1)
The first two lines are the base cases that stop the recursion (the grid's equivalent of "hitting the wall"). Marking as visited is what prevents going back and falling into an infinite loop.
For Number of Islands: walk the grid; every time you find land not yet visited, it is a new island (+1), and you trigger a DFS that sinks the entire island. Since the DFS marks everything that is connected, each island is counted exactly once. Watch the DFS flooding — the current cell highlighted, the blue showing what has already been visited:
Why is it O(m·n) time and O(m·n) space?
- Time: the outer loop goes through all the cells; the DFS visits each cell at most once in total (once marked, it is ignored). Adding up, each cell is processed a constant number of times → O(m·n).
- Space: the
visitedgrid is O(m·n); and the recursion stack can reach O(m·n) in the worst case (an island that snakes through all the cells). You can save space by marking in the grid itself (sinking the land into water) instead of a separate grid.
Recursive DFS = implicit stack
Recursion uses the call stack to remember where to return — it is the same
stack you saw in the String section, now working for you for free. You can
write the same DFS iteratively with an explicit stack (stack), useful
when the recursion gets too deep and overflows. Both forms explore in the same
"go deep first" order.
Where DFS on a grid shows up
Counting/measuring connected regions (islands, maximum island area), filling regions (flood fill, capturing surrounded regions), and detecting shapes. Whenever the question is about "what is connected to what" in a grid, DFS (or BFS, from lesson 6) is the tool. And that is exactly the bridge to the Graphs section: the grid is just a graph with regular neighborhoods.
🧠 Review quiz
0/41. Why do we treat a grid as a graph in flood fill?
2. In Number of Islands, why does each DFS count exactly one island?
3. What is the role of 'marking as visited' in DFS on a grid?
4. Recursive DFS uses the call stack. What does that imply about space?
Summary
- A grid is a graph; flood fill with DFS explores an entire connected region.
- Base cases (outside the grid / not part of it / already visited) stop the recursion; marking visited avoids a loop.
- Number of Islands: +1 and a DFS for each new piece of land → each island counted once.
- O(m·n) time; O(m·n) space (visited + recursion stack).
Next lesson: when the path matters and can be undone — backtracking.