Understand
30-second explanation
Build the interview-ready mental model before comparing implementations.
From the top-left cell, try to reach the bottom-right by stepping only onto open cells. Backtrack: at each cell try each allowed direction in order, mark the cell visited, recurse, and unmark it when you back out so other paths can reuse it. To find one path, return as soon as you reach the goal; to count or list all paths, keep going after each success. With 4 directions the worst case is O(4^(R·C))-ish, pruned hard by walls and the visited grid.
Interview contextWhat is testedWhere candidates slipHow to stand outExplore interview contextHide interview context
What's being tested
Whether you can write clean grid backtracking with correct mark/unmark, bounds and wall checks, and adapt between 'find one path' and 'count/list all paths'.
Where candidates slip
Marking a cell visited but never unmarking on the way out, which blocks alternative paths, or checking bounds after indexing the grid (out-of-range access).
How to stand out
State the choose-explore-unchoose structure, fix a deterministic direction order, and explain how the same skeleton switches between one-path and all-paths by changing the return behavior.
Choose
Compare the available approaches
Use constraints and trade-offs to select the route you can defend in an interview.
Interactive decision treeInteractiveChoose the right approachFirst decision previewReach bottom-right in a grid?Need shortest path only?YesNo, all moves / enumerateExplore the full treeCollapse the tree
Learn & trace
Build each solution step by step
Move from the core idea to a visual trace, implementation, proof, and edge cases.
Approach 1 of 2
Down/Right-Only DFS (Simplest Case)
1Hint 1Explore hintHide hint
If the rat may only move down or right, can it ever revisit a cell? What does that mean for the visited grid?
2Hint 2Explore hintHide hint
Recurse: at (r, c) try moving right to (r, c+1) and down to (r+1, c) if those cells are open and in bounds.
3Hint 3Explore hintHide hint
Because moves are monotonic, no cycle is possible, so you don't need to mark/unmark — just recurse and OR the results.
A · Core idea
Handle the restricted version first: the rat moves only right or down.
No cycles, no marking. Right and down both strictly increase a coordinate, so the rat can never return to a visited cell. That removes the need for a visited grid and the unmark step — the structure simplifies to plain DFS.
The recursion. From (r, c), if it's the goal return true. Otherwise try moving right and down; return true if either reaches the goal through open, in-bounds cells.
Why mention it. It isolates the core 'try each move, recurse' idea before adding the complication of cycles. It also opens the door to DP: since subproblems repeat, memoizing on (r, c) collapses the exponential tree to O(R·C).
Limitation. It only works when backward moves are forbidden. The moment Left/Up are allowed, cycles appear and you need the full visited-and-backtrack machinery.
B · Visual dry run
Interactive dry run
Select a frame to connect the decision with its visual state.
maze = [[1,1],[0,1]]Interactive diagram
Down/right recursion tree
(0,0)Each node branches into right and down; one branch reaches the goal.
trueC · Code with teaching comments
Reference implementation previewpublic boolean hasPathDownRight(int[][] maze) { int R = maze.length, C = maze[0].length; if (maze[0][0] == 0 || maze[R - 1][C - 1] == 0) return false; return dfs(maze, 0, 0, R, C); }Explore the full codeHide the full code
D · Why it works and complexityO(2^(R+C)) timeO(R + C) spaceOpen the invariant and proofExplore the proofHide the proof
Restricting moves to down/right removes cycles entirely, which is why this variant needs no backtracking. Recognizing when a problem is acyclic tells you whether you can drop the mark/unmark bookkeeping (and even switch to DP).
ComplexityO(2^(R+C)) time, O(R + C) space — Each call branches into at most 2 moves and the path length is R + C - 1, so the tree has O(2^(R+C)) leaves in the worst case. Recursion depth is O(R + C). (With memoization on (r, c) this drops to O(R·C).)
E · Edge cases and pitfalls3Indexing maze[r][c] before the bounds check — out-of-range crash.Explore edge casesHide edge cases
Pitfalls
- Indexing maze[r][c] before the bounds check — out-of-range crash.
- Adding it to a four-direction problem — cycles appear and this version loops or misses paths.
- Skipping the walled-endpoint guard.
Approach 2 of 2 · Optimal
Four-Direction Backtracking (Optimal General Case)
1Hint 1Explore hintHide hint
With all four moves the rat can loop. How do you stop it from stepping back onto a cell already on the current path?
2Hint 2Explore hintHide hint
Keep a visited grid. Mark (r, c) before recursing into neighbors; unmark it after all four directions are tried.
3Hint 3Explore hintHide hint
If you reach the goal, you've found a path — return true to stop, or record the move string and continue if you must list every path.
A · Core idea
Run depth-first search that records which cells are on the current path and rolls each choice back.
Choose. At cell (r, c), first reject it if it's off the grid, a wall, or already on the current path. If it's the goal, you've found a path. Otherwise mark (r, c) as visited — it's now part of the path you're building.
Explore. Try the four directions in a fixed order (Down, Left, Right, Up). Each recursive call attempts to extend the path through that neighbor. Using a consistent order makes any listed paths come out in lexicographic order.
Unchoose (the heart of backtracking). After all four directions return, unmark (r, c). This is the step beginners drop. Without it, a cell explored on one dead-end path stays blocked forever, so genuinely valid paths that reuse that cell are never found. Unmarking restores the grid to exactly its state before this call, so sibling branches start clean.
One path vs all paths. To answer 'does a path exist', propagate a boolean and return true as soon as the goal is hit — the early return prunes the rest. To count or list every path, don't early-return on success: record the path (or increment a counter) and keep exploring other directions; the unmark step is what makes enumerating distinct paths correct.
Why the visited grid is mandatory here. With Left and Up allowed, two open cells form a cycle. Without the visited check the rat oscillates between them forever. Marking the current path breaks every cycle while still allowing different paths to share cells across separate branches.
B · Visual dry run
Interactive dry run
Select a frame to connect the decision with its visual state.
maze = [[1,0,0],[1,1,0],[0,1,1]], goal (2,2)Interactive diagram
Backtracking: dead end then unmark
(0,0)A dead-end branch unmarks its cells, freeing them for the branch that reaches the goal.
trueC · Code with teaching comments
D · Why it works and complexityO(4^(R·C)) timeO(R·C) spaceOpen the invariant and proofExplore the proofHide the proof
Backtracking is 'choose, explore, un-choose'. The un-choose is not optional cleanup — it's what separates 'cells on the current path' from 'cells permanently used'. Mark before the recursive calls, unmark after, and a single grid serves every candidate path without interference.
ComplexityO(4^(R·C)) time, O(R·C) space — Each cell branches into up to 4 directions, and a path can touch up to R·C cells, giving a worst-case O(4^(R·C)) search tree. Walls and the visited grid prune this drastically in practice. The visited grid and recursion stack are O(R·C) space.
E · Edge cases and pitfalls7start (0,0) is a wall — Front guard returns false immediately.Explore edge casesHide edge cases
Edge cases handled
start (0,0) is a wallFront guard returns false immediately.1×1 open grid [[1]]Start equals goal → true with an empty move sequence.fully blocked except startAll four neighbors are walls; after trying them, unmark and return false.isolated goal surrounded by wallsSearch exhausts all paths, none reach the goal → false.
Pitfalls
- Marking visited but never unmarking — permanently walls off cells and misses valid paths.
- Checking maze[r][c] before the bounds check — index out of range.
- Early-returning on the first goal when the task is to COUNT or LIST all paths.
Practice
Try the solution yourself
Edit the optimal implementation, test another input, and observe the result.
Editable code previewprivate static final int[] DR = {1, 0, 0, -1}; // D, L, R, U private static final int[] DC = {0, -1, 1, 0}; public boolean hasPath(int[][] maze) { int R = maze.length, C = maze[0].length;Explore the playgroundClose the playground
Avoid mistakes
Recognize the interview traps
Understand why common attempts fail before reviewing the corrected implementation.
1Missing the backtrack (no unmark)
If you mark a cell but never clear it, a dead-end exploration leaves the cell flagged as on-path forever, so a different route that needs that cell can never proceed.
Explore the code comparisonHide the code comparison
Missing the backtrack (no unmark)
If you mark a cell but never clear it, a dead-end exploration leaves the cell flagged as on-path forever, so a different route that needs that cell can never proceed.
Don't write this
visited[r][c] = True
for dr, dc in zip(DR, DC):
if solve(r + dr, c + dc):
return True
return False # WRONG: visited[r][c] never resetWrite this instead
visited[r][c] = True
for dr, dc in zip(DR, DC):
if solve(r + dr, c + dc):
return True
visited[r][c] = False # restore before returning
return False2Bounds check after indexing
Reading maze[r][c] before confirming r and c are in range throws an out-of-bounds error the moment the rat steps off the edge.
Explore the code comparisonHide the code comparison
Bounds check after indexing
Reading maze[r][c] before confirming r and c are in range throws an out-of-bounds error the moment the rat steps off the edge.
Don't write this
if (maze[r][c] == 0) return false; // crashes if r,c off grid
if (r < 0 || r >= R || c < 0 || c >= C) return false;Write this instead
if (r < 0 || r >= R || c < 0 || c >= C) return false;
if (maze[r][c] == 0) return false; // safe nowRevise
Carry the pattern into the interview
Pattern to remember
Grid backtracking: choose a direction, recurse, undo the choice
Code pattern to memorize
solve(r, c):
if out of bounds or blocked or visited: return
if (r, c) == goal: record path; return
mark visited
for each allowed (dr, dc):
solve(r + dr, c + dc)
unmark visited # backtrackRules to remember
- 1Mark a cell before recursing and unmark after — forgetting the unmark turns one bad path into a permanent wall.
- 2Bounds-check and wall-check before stepping; treat off-grid as blocked.
- 3Fix a direction order (e.g. D, L, R, U) so 'lexicographically smallest path' is well defined.
- 4To find one path, propagate a boolean success and stop early; to count all, return after exploring every direction without early exit.
- 5The visited grid prevents cycles — without it the rat can loop forever between two open cells.
Use this pattern when
- Find or count paths in a grid with blocked cells and limited moves
- Enumerate all routes, not just the shortest one
- Constraints make the grid small (backtracking is exponential)
- Path must obey movement rules (only down/right, or all four directions)
Choose another pattern when
- Only need the SHORTEST path length → BFS in O(R·C), not backtracking
- Grid is large and you need any path fast → BFS/DFS with a visited set, no enumeration
- Counting paths with only down/right moves and no obstacles → closed-form combinatorics or DP
- Weighted cells / minimum cost → Dijkstra or DP, not raw backtracking
Interview takeaway
Rat in a Maze is the template for grid backtracking: mark, explore each direction, unmark. The mark/unmark pairing is the whole trick — it lets a cell belong to many candidate paths without polluting the others.
Continue
Transfer the pattern to another problem
Use a direct variation or pattern sibling to prove the idea is reusable.
Transferable patternRat in a Maze is the canonical grid-backtracking template: choose a cell, explore each direction, un-choose. The mark/unmark pairing recurs in Word Search, Unique Paths III, and N-Queens-style constraint search.
Practice queueDirect variations and pattern siblings
Direct follow-ups
- Word SearchSame grid backtracking with mark/unmark, matching letters instead of reaching a goal cell.
- Unique PathsComing soonDown/right only and no obstacles → count paths with DP or a binomial coefficient.
- Unique Paths IIIComing soonBacktracking that must visit every open cell exactly once before reaching the end.
- Shortest Path in Binary MatrixComing soonWhen only the shortest path matters, switch from backtracking to BFS for O(R·C).