Off-by-one on range boundaries
Wrong move: Loop endpoints miss first/last candidate.
Usually fails on: Fails on minimal arrays and exact-boundary answers.
Fix: Re-derive loops from inclusive/exclusive ranges before coding.
Break down a hard problem into reliable checkpoints, edge-case handling, and complexity trade-offs.
You are given an m x n integer array grid where grid[i][j] could be:
1 representing the starting square. There is exactly one starting square.2 representing the ending square. There is exactly one ending square.0 representing empty squares we can walk over.-1 representing obstacles that we cannot walk over.Return the number of 4-directional walks from the starting square to the ending square, that walk over every non-obstacle square exactly once.
Example 1:
Input: grid = [[1,0,0,0],[0,0,0,0],[0,0,2,-1]] Output: 2 Explanation: We have the following two paths: 1. (0,0),(0,1),(0,2),(0,3),(1,3),(1,2),(1,1),(1,0),(2,0),(2,1),(2,2) 2. (0,0),(1,0),(2,0),(2,1),(1,1),(0,1),(0,2),(0,3),(1,3),(1,2),(2,2)
Example 2:
Input: grid = [[1,0,0,0],[0,0,0,0],[0,0,0,2]] Output: 4 Explanation: We have the following four paths: 1. (0,0),(0,1),(0,2),(0,3),(1,3),(1,2),(1,1),(1,0),(2,0),(2,1),(2,2),(2,3) 2. (0,0),(0,1),(1,1),(1,0),(2,0),(2,1),(2,2),(1,2),(0,2),(0,3),(1,3),(2,3) 3. (0,0),(1,0),(2,0),(2,1),(2,2),(1,2),(1,1),(0,1),(0,2),(0,3),(1,3),(2,3) 4. (0,0),(1,0),(2,0),(2,1),(1,1),(0,1),(0,2),(0,3),(1,3),(1,2),(2,2),(2,3)
Example 3:
Input: grid = [[0,1],[2,0]] Output: 0 Explanation: There is no path that walks over every empty square exactly once. Note that the starting and ending square can be anywhere in the grid.
Constraints:
m == grid.lengthn == grid[i].length1 <= m, n <= 201 <= m * n <= 20-1 <= grid[i][j] <= 2Problem summary: You are given an m x n integer array grid where grid[i][j] could be: 1 representing the starting square. There is exactly one starting square. 2 representing the ending square. There is exactly one ending square. 0 representing empty squares we can walk over. -1 representing obstacles that we cannot walk over. Return the number of 4-directional walks from the starting square to the ending square, that walk over every non-obstacle square exactly once.
Start with the most direct exhaustive search. That gives a correctness anchor before optimizing.
Pattern signal: Array · Backtracking · Bit Manipulation
[[1,0,0,0],[0,0,0,0],[0,0,2,-1]]
[[1,0,0,0],[0,0,0,0],[0,0,0,2]]
[[0,1],[2,0]]
sudoku-solver)unique-paths-ii)word-search-ii)Source-backed implementations are provided below for direct study and interview prep.
// Accepted solution for LeetCode #980: Unique Paths III
class Solution {
private int m;
private int n;
private int cnt;
private int[][] grid;
private boolean[][] vis;
public int uniquePathsIII(int[][] grid) {
m = grid.length;
n = grid[0].length;
this.grid = grid;
int x = 0, y = 0;
for (int i = 0; i < m; ++i) {
for (int j = 0; j < n; ++j) {
if (grid[i][j] == 0) {
++cnt;
} else if (grid[i][j] == 1) {
x = i;
y = j;
}
}
}
vis = new boolean[m][n];
vis[x][y] = true;
return dfs(x, y, 0);
}
private int dfs(int i, int j, int k) {
if (grid[i][j] == 2) {
return k == cnt + 1 ? 1 : 0;
}
int ans = 0;
int[] dirs = {-1, 0, 1, 0, -1};
for (int h = 0; h < 4; ++h) {
int x = i + dirs[h], y = j + dirs[h + 1];
if (x >= 0 && x < m && y >= 0 && y < n && !vis[x][y] && grid[x][y] != -1) {
vis[x][y] = true;
ans += dfs(x, y, k + 1);
vis[x][y] = false;
}
}
return ans;
}
}
// Accepted solution for LeetCode #980: Unique Paths III
func uniquePathsIII(grid [][]int) int {
m, n := len(grid), len(grid[0])
cnt := 0
vis := make([][]bool, m)
x, y := 0, 0
for i, row := range grid {
vis[i] = make([]bool, n)
for j, v := range row {
if v == 0 {
cnt++
} else if v == 1 {
x, y = i, j
}
}
}
dirs := [5]int{-1, 0, 1, 0, -1}
var dfs func(i, j, k int) int
dfs = func(i, j, k int) int {
if grid[i][j] == 2 {
if k == cnt+1 {
return 1
}
return 0
}
ans := 0
for h := 0; h < 4; h++ {
x, y := i+dirs[h], j+dirs[h+1]
if x >= 0 && x < m && y >= 0 && y < n && !vis[x][y] && grid[x][y] != -1 {
vis[x][y] = true
ans += dfs(x, y, k+1)
vis[x][y] = false
}
}
return ans
}
vis[x][y] = true
return dfs(x, y, 0)
}
# Accepted solution for LeetCode #980: Unique Paths III
class Solution:
def uniquePathsIII(self, grid: List[List[int]]) -> int:
def dfs(i: int, j: int, k: int) -> int:
if grid[i][j] == 2:
return int(k == cnt + 1)
ans = 0
for a, b in pairwise(dirs):
x, y = i + a, j + b
if 0 <= x < m and 0 <= y < n and (x, y) not in vis and grid[x][y] != -1:
vis.add((x, y))
ans += dfs(x, y, k + 1)
vis.remove((x, y))
return ans
m, n = len(grid), len(grid[0])
start = next((i, j) for i in range(m) for j in range(n) if grid[i][j] == 1)
dirs = (-1, 0, 1, 0, -1)
cnt = sum(row.count(0) for row in grid)
vis = {start}
return dfs(*start, 0)
// Accepted solution for LeetCode #980: Unique Paths III
struct Solution;
impl Solution {
fn unique_paths_iii(mut grid: Vec<Vec<i32>>) -> i32 {
let mut res = 0;
let n = grid.len();
let m = grid[0].len();
let mut count = 0;
for i in 0..n {
for j in 0..m {
if grid[i][j] == 0 {
count += 1;
}
}
}
for i in 0..n {
for j in 0..m {
if grid[i][j] == 1 {
Self::dfs(i, j, count, &mut res, &mut grid, n, m);
}
}
}
res as i32
}
fn dfs(
i: usize,
j: usize,
left: usize,
all: &mut usize,
grid: &mut Vec<Vec<i32>>,
n: usize,
m: usize,
) {
match grid[i][j] {
2 => {
if left == 0 {
*all += 1;
}
}
1 => {
grid[i][j] = -1;
for (r, c) in Self::adj(i, j, n, m) {
Self::dfs(r, c, left, all, grid, n, m);
}
grid[i][j] = 1;
}
0 => {
grid[i][j] = -1;
for (r, c) in Self::adj(i, j, n, m) {
Self::dfs(r, c, left - 1, all, grid, n, m);
}
grid[i][j] = 0;
}
_ => {}
}
}
fn adj(i: usize, j: usize, n: usize, m: usize) -> Vec<(usize, usize)> {
let mut res = vec![];
if i > 0 {
res.push((i - 1, j));
}
if j > 0 {
res.push((i, j - 1));
}
if i + 1 < n {
res.push((i + 1, j));
}
if j + 1 < m {
res.push((i, j + 1));
}
res
}
}
#[test]
fn test() {
let grid = vec_vec_i32![[1, 0, 0, 0], [0, 0, 0, 0], [0, 0, 2, -1]];
let res = 2;
assert_eq!(Solution::unique_paths_iii(grid), res);
let grid = vec_vec_i32![[1, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 2]];
let res = 4;
assert_eq!(Solution::unique_paths_iii(grid), res);
let grid = vec_vec_i32![[0, 1], [2, 0]];
let res = 0;
assert_eq!(Solution::unique_paths_iii(grid), res);
}
// Accepted solution for LeetCode #980: Unique Paths III
function uniquePathsIII(grid: number[][]): number {
const m = grid.length;
const n = grid[0].length;
let [x, y] = [0, 0];
let cnt = 0;
for (let i = 0; i < m; ++i) {
for (let j = 0; j < n; ++j) {
if (grid[i][j] === 0) {
++cnt;
} else if (grid[i][j] == 1) {
[x, y] = [i, j];
}
}
}
const vis: boolean[][] = Array.from({ length: m }, () => Array(n).fill(false));
vis[x][y] = true;
const dirs = [-1, 0, 1, 0, -1];
const dfs = (i: number, j: number, k: number): number => {
if (grid[i][j] === 2) {
return k === cnt + 1 ? 1 : 0;
}
let ans = 0;
for (let d = 0; d < 4; ++d) {
const [x, y] = [i + dirs[d], j + dirs[d + 1]];
if (x >= 0 && x < m && y >= 0 && y < n && !vis[x][y] && grid[x][y] !== -1) {
vis[x][y] = true;
ans += dfs(x, y, k + 1);
vis[x][y] = false;
}
}
return ans;
};
return dfs(x, y, 0);
}
Use this to step through a reusable interview workflow for this problem.
Generate every possible combination without any filtering. At each of n positions we choose from up to n options, giving nⁿ total candidates. Each candidate takes O(n) to validate. No pruning means we waste time on clearly invalid partial solutions.
Backtracking explores a decision tree, but prunes branches that violate constraints early. Worst case is still factorial or exponential, but pruning dramatically reduces the constant factor in practice. Space is the recursion depth (usually O(n) for n-level decisions).
Review these before coding to avoid predictable interview regressions.
Wrong move: Loop endpoints miss first/last candidate.
Usually fails on: Fails on minimal arrays and exact-boundary answers.
Fix: Re-derive loops from inclusive/exclusive ranges before coding.
Wrong move: Mutable state leaks between branches.
Usually fails on: Later branches inherit selections from earlier branches.
Fix: Always revert state changes immediately after recursive call.