What it is
A grid problem treats every cell as a node in an implicit graph, where two cells are connected if they're adjacent (orthogonally, or with diagonals allowed) and satisfy some rule about their values. Once you see that, the technique is whichever graph tool fits: DFS/BFS flood fill to find connected components, multi-source BFS to compute distance-to-nearest-something, or backtracking DFS to trace a constrained path through the grid. A smaller slice of grid problems isn't graph search at all but a 2D dynamic program, where a cell's answer is built from its up/left/diagonal neighbors' already-computed answers.
When it applies
- The problem is about connectivity or reachability between cells under an adjacency rule — counting groups, flooding a region, checking if two edges of the grid are linked.
- The problem asks for a shortest path or minimum time to reach cells from one or several starting points, with uniform per-step cost — that's BFS, seeded with every starting cell at once when there are several.
- The problem builds a value at each cell purely from a small fixed set of already-computed neighbor values (the cell above, to the left, and the diagonal) — that's a 2D DP, not a search, even though the grid shape looks the same.
- It does not apply as a search when step costs differ (diagonal moves costing more, some cells being "expensive") — that calls for Dijkstra over the same adjacency, not plain BFS.
Tells in the problem
- "Count the number of connected groups/regions/islands" — flood fill each unvisited node once, counting starts.
- "Every minute, X spreads to adjacent cells" or "minimum time until every cell is reached" — multi-source BFS seeded with all initial sources simultaneously, not one search per source.
- "Determine which cells can reach both edges/boundaries" — run the reachability search backwards from each boundary and intersect, rather than simulating forward from every cell.
- "Trace a word/path through the grid, visiting each cell at most once" — backtracking DFS with explicit mark-on-enter, unmark-on-exit discipline.
- "Largest square/rectangle of a given value" — a 2D DP keyed on a cell's bottom-right corner, reading three already-computed neighbors, not a search at all.
The classic mistake
The recurring bug is running a separate search from every cell or every individual source instead of recognizing that a single multi-source search (or one reversed search) answers the whole grid at once — this shows up as accidentally writing an O((rows·cols)²) solution when a linear one exists, because the naive framing ("for each cell, find its nearest gate") looks correct but throws away the shared work between neighboring cells. The second common bug is marking a cell visited when it's dequeued rather than when it's enqueued, which lets the same cell enter the queue many times and can blow up both time and (for backtracking DFS) correctness if the mark represents "in use." Before coding, ask whether the search should start from one cell or from every relevant cell at once, and mark state the moment a cell is scheduled for visiting, not when it's finally processed.
Questions to start with
- Number of Islands — the baseline flood fill: an implicit graph, connected components, mark cells the moment you enqueue them.
- Rotting Oranges — the same flood fill generalized to multiple starting sources at once, with level-by-level BFS standing in for elapsed time.
- Word Search — a different tool entirely: backtracking DFS along a constrained path, where the discipline is marking cells in-use and restoring them on the way back.
- Pacific Atlantic Water Flow — the reachability search runs backwards from two boundaries instead of forwards from every cell, which is what turns an O(n²) brute force into a linear one.
- Maximal Square — the non-search case: a cell's answer comes from three already-computed neighbors, which is a 2D DP wearing a grid's clothing.