Problem Statement in English
You’re given a $9x9$ Sudoku board, where some of the cells are filled with digits from ‘1’ to ‘9’ and others are empty (represented by ‘.’). The goal is to fill the empty cells such that each row, each column, and each of the nine $3x3$ sub-boxes contains all of the digits from ‘1’ to ‘9’ exactly once.
Approach
We must try out all possible combinations of numbers in the empty cells, ensuring that we adhere to the Sudoku rules. This is a classic backtracking problem. Although we can make some optimisations.
We should track the numbers already present in each row, column, and 3x3 box. This allows us to check the validity of placing a number in constant time, rather than scanning the entire row, column, or box.
So we maintain three sets for each row, column, and box to keep track of the numbers that have already been placed. When we attempt to place a number in an empty cell, we check these sets to see if the number is already present. If it is not, we place the number and update the sets accordingly. If we reach a point where no valid number can be placed, we backtrack by removing the last placed number from the board and the sets, and continue with the next possibility.
A handy formula to map a cell from a grid to a 1D array in general is:
$\text{index} = \text{row} \cdot \text{number of columns} + \text{column}$.
To reverse the mapping, we can use:
$\text{row} = \left\lfloor \dfrac{\text{index}}{\text{number of columns}} \right\rfloor$
$\text{column} = \text{index} \bmod \text{number of columns}$
And we’re done!
Solution in Python
class Solution:
def solveSudoku(self, board: List[List[str]]) -> None:
# Pre-track numbers already in rows, cols, and 3x3 boxes
rows = [set() for _ in range(9)]
cols = [set() for _ in range(9)]
boxes = [set() for _ in range(9)]
# Initialize tracking state from the initial board
for r in range(9):
for c in range(9):
if board[r][c] != '.':
val = board[r][c]
rows[r].add(val)
cols[c].add(val)
boxes[(r // 3) * 3 + (c // 3)].add(val)
def solve():
for row in range(9):
for col in range(9):
if board[row][col] == '.':
box_idx = (row // 3) * 3 + (col // 3)
for num in '123456789':
# O(1) validity check
if num not in rows[row] and num not in cols[col] and num not in boxes[box_idx]:
# Place choice
board[row][col] = num
rows[row].add(num)
cols[col].add(num)
boxes[box_idx].add(num)
if solve():
return True
# Backtrack choice
board[row][col] = '.'
rows[row].remove(num)
cols[col].remove(num)
boxes[box_idx].remove(num)
return False # Trigger backtracking if no digits fit
return True # Board complete
solve()
Complexity
Time: $O(9^{n})$
where $n$ is the number of empty cells in the Sudoku board. In the worst case, we may have to try all 9 digits for each empty cell, leading to an exponential time complexity.Space: $O(9)$
where 9 is the size of the Sudoku board. This is because we are using sets to track the numbers in rows, columns, and boxes, which takes constant space.
Mistakes I Made
My solution was overcomplicated
And we are done.