πΉ Problem: 37 Sudoku Solver
Difficulty: #Hard
Tags: #Backtracking, #Matrix, #Recursion
π Problem Summary
We are given a partially filled 9Γ9 Sudoku board. The task is to fill the empty cells (
.
) such that the Sudoku rules are satisfied:
- Each row contains digits
1β9
without repetition- Each column contains digits
1β9
without repetition- Each of the nine 3Γ3 sub-boxes contains digits
1β9
without repetitionThe solution must modify the board in-place.
π§ My Thought Process
Brute Force Idea:
Try all numbers1β9
in every empty cell and check if the board remains valid. This would be extremely slow because there are many possibilities.-
Optimized Strategy:
Use backtracking with constraints tracking:- Sudoku is already solved partially in programming, So, this problem is more like a algorithm memorization problem.
- Maintain three data structures (
rows
,cols
,boxes
) to quickly check if a number can be placed. - Instead of recomputing validity each time, update these structures when placing/removing numbers.
- Recursively try to fill the board cell by cell.
- If a placement leads to a dead end, undo (backtrack) and try another number.
-
Algorithm Used:
Backtracking with pruning using helper functions:-
couldPlace(val, row, col)
β checks if we can place a number. -
placeNumber(val, row, col)
/removeNumber(...)
β update board + tracking arrays. -
backtrack(row, col)
β recursive filling with backtracking.
-
βοΈ Code Implementation (Python)
class Solution:
def solveSudoku(self, board: List[List[str]]) -> None:
"""
Modify the board in-place using backtracking.
"""
n, N = 3, 9
rows = [[0] * (N+1) for _ in range(N)]
cols = [[0] * (N+1) for _ in range(N)]
boxes = [[0] * (N+1) for _ in range(N)]
solved = False
def couldPlace(val, row, col):
idx = (row // 3) * 3 + col // 3
return rows[row][val] + cols[col][val] + boxes[idx][val] == 0
def placeNumber(val, row, col):
idx = (row // 3) * 3 + col // 3
rows[row][val] += 1
cols[col][val] += 1
boxes[idx][val] += 1
board[row][col] = str(val)
def removeNumber(val, row, col):
idx = (row // 3) * 3 + col // 3
rows[row][val] -= 1
cols[col][val] -= 1
boxes[idx][val] -= 1
board[row][col] = '.'
def placeNext(row, col):
nonlocal solved
if row == N-1 and col == N-1:
solved = True
elif col == N-1:
backtrack(row+1, 0)
else:
backtrack(row, col+1)
def backtrack(row, col):
nonlocal solved
if board[row][col] == '.':
for val in range(1, 10):
if couldPlace(val, row, col):
placeNumber(val, row, col)
placeNext(row, col)
if not solved:
removeNumber(val, row, col)
else:
placeNext(row, col)
# Initialize constraints from given board
for i in range(N):
for j in range(N):
if board[i][j] != '.':
placeNumber(int(board[i][j]), i, j)
backtrack(0, 0)
β±οΈ Time & Space Complexity
-
Time: O(9^m) worst case, where
m
is the number of empty cells. Pruning (checking validity with sets/arrays) makes it much faster in practice. - Space: O(81) = O(1) (fixed-size board + tracking arrays).
π§© Key Takeaways
- β Backtracking is powerful for constraint satisfaction problems like Sudoku.
- π‘ The row, col, box tracking arrays significantly reduce redundant checks.
- π When faced with recursive filling problems, always think: βCan I prune invalid paths early with some data structure?β
π Reflection (Self-Check)
- [x] Could I solve this without help? (with some practice)
- [x] Did I write code from scratch?
- [x] Did I understand why it works?
- [ ] Will I be able to recall this in a week? (I haven't memorized the exact code yet)
π Related Problems
- [[notes/36 Valid Sudoku]]
- [[notes/980 Unique Paths III]]
π Progress Tracker
Metric | Value |
---|---|
Day | 72 |
Total Problems Solved | 433 |
Confidence Today | π |
Leetcode Rating | 1530 |
Top comments (0)