Sudoku Solver
Fill every empty cell of a 9 × 9 Sudoku grid so the completed board is valid.
A board is valid when each digit 1 through 9 appears exactly once in every row, exactly once in every column, and exactly once in each of the nine 3 × 3 boxes.
Empty cells are given as the character '.'. The board is modified in place — the function returns nothing, and the caller reads the solution out of the grid it passed in. The input is guaranteed to have exactly one solution.
- board.length == 9 and board[i].length == 9
- board[i][j] is a digit character '1'–'9' or the character '.'
- The given board is valid — no conflicts exist among the pre-filled digits
- Exactly one solution is guaranteed to exist
- The board must be modified in place rather than returned
A row reading 5 3 . . 7 . . . . with the first empty cell at (0,2)That cell becomes 4An empty cell whose row, column and box between them contain all of 1–9No digit can be placed — the search backtracksA grid with only one empty cell remainingThe single missing digit is placed and the puzzle is completeA completely filled valid boardUnchanged — the function returns immediatelyFill every empty cell so each digit 1–9 appears once per row, once per column, and once per 3×3 box. Only one solution exists, so we want the first complete board: the recursion returns True/False and stops the instant it wins. And because the finished board is the answer, we do not undo on success.
Every problem in this section is the same loop — three beats: choose, explore, un-choose.
def backtrack(state):
if done(state):
save(state) # record the finished answer
return
for choice in choices(state): # what can I pick right now?
if not ok(choice):
continue # prune: skip bad picks early
apply(choice) # CHOOSE
backtrack(next_state) # EXPLORE
undo(choice) # UN-CHOOSE: put it backOnly four slots change between problems: done, choices, ok (pruning), and how you finish (save every answer, or return True at the first one). Fill those and the problem is solved.
1–9 into the first empty cell.1–9.def solve_sudoku(board):
def ok(r, c, ch):
for i in range(9):
if board[r][i] == ch or board[i][c] == ch:
return False
if board[3*(r//3) + i//3][3*(c//3) + i%3] == ch: # 3x3 box
return False
return True
def backtrack():
for r in range(9):
for c in range(9):
if board[r][c] != '.':
continue
for ch in "123456789":
if not ok(r, c, ch):
continue # PRUNE
board[r][c] = ch # CHOOSE
if backtrack(): # EXPLORE
return True # solved — do NOT undo
board[r][c] = '.' # UN-CHOOSE (only on failure)
return False # no digit fit: dead end, back up
return True # no empty cell: done
backtrack()Two rules unique to "find one answer": return True straight up the chain and don't undo (undoing would erase the solution); and return False after the digit loop, which tells the caller "back up and try something else." A cell's box is (r//3, c//3).
Trace: at the first empty cell, digits 1/2/3 clash with the row/column/box, 4 is clean → place 4, recurse; a later dead-end returns False, undoes, and tries the next digit until the board fills.
Sudoku Solver Intuition
MINDSET
Sudoku is about **Constraint Satisfaction**. We don't just guess; we systematically fill gaps and immediately abandon any path that violates the row, column, or 3x3 box rules.
PRUNING
Backtracking works because it prunes trillions of branches. If a '5' is invalid at [0,0], we never even look at the $9^80$ possible boards that start with '5' at that position.