Problem
Given a 9ร9 Sudoku board filled with digits 1โ9 and dots for empty cells, decide whether it's currently valid. A board is valid if every row, every column, and every 3ร3 sub-box contains each digit at most once โ you only check the filled cells, not whether the puzzle is actually solvable.
5 3 . | . 7 . | . . .
6 . . | 1 9 5 | . . .
. 9 8 | . . . | . 6 .
------+-------+------
8 . . | . 6 . | . . 3
4 . . | 8 . 3 | . . 1
7 . . | . 2 . | . . 6
------+-------+------
. 6 . | . . . | 2 8 .
. . . | 4 1 9 | . . 5
. . . | . 8 . | . 7 9
- Input: the board above
- Output:
true - Explanation: no digit repeats in any row, column, or 3ร3 box
Counter-example: if the top-left box contained two 5s (e.g., a second 5 at row 1, col 0), the board would be invalid even if every row and column still looked clean individually.
Intuition
Because the board is always 9ร9, every operation here is constant-time โ correctness is the only challenge. The key insight is that all three constraint types (rows, columns, 3ร3 boxes) can be validated in a single left-to-right, top-to-bottom pass: maintain one hash set per row, column, and box, and whenever you encounter a filled cell, check its digit against all three sets before recording it.
Solution โ Single-Pass Hash Sets
Allocate three arrays of nine sets each โ one array for rows, one for columns, one for boxes. Scan every cell; if a digit is already in any of its corresponding sets, the board is invalid.
- Create
row_sets[9],col_sets[9], andbox_sets[9], each containing empty sets. - Iterate over all 81 cells row by row, left to right.
- Skip any cell whose value is
'.'. - Compute
box_index = (row // 3) * 3 + (col // 3)โ this maps each cell to one of nine boxes numbered 0โ8. - If the digit already exists in
row_sets[row],col_sets[col], orbox_sets[box_index], returnfalse. - Otherwise add the digit to all three sets and continue.
- Return
trueafter scanning all cells without conflict.
1def isValidSudoku(board: list[list[str]]) -> bool:
2 row_sets = [set() for _ in range(9)]
3 col_sets = [set() for _ in range(9)]
4 box_sets = [set() for _ in range(9)]
5
6 for row in range(9):
7 for col in range(9):
8 digit = board[row][col]
9 if digit == '.':
10 continue
11
12 # maps each cell to one of nine 3ร3 boxes (0 = top-left, 8 = bottom-right)
13 box_index = (row // 3) * 3 + (col // 3)
14
15 if (digit in row_sets[row]
16 or digit in col_sets[col]
17 or digit in box_sets[box_index]):
18 return False
19
20 row_sets[row].add(digit)
21 col_sets[col].add(digit)
22 box_sets[box_index].add(digit)
23
24 return TrueTime: O(1) โ the board is always 9ร9, so we visit exactly 81 cells with constant work each.
Space: O(1) โ 27 sets holding at most 9 digits each; the total never exceeds 243 entries.
Complexity Summary
| Approach | Time | Space | When to use |
|---|---|---|---|
| Single-Pass Hash Sets | O(1) | O(1) | Always โ this is the canonical solution for the fixed 9ร9 board |
Common Mistakes
- Swapping row and column in the box formula โ
(row // 3) * 3 + (col // 3)is correct; flipping to(col // 3) * 3 + (row // 3)produces the same nine integers but maps different cells to the same box, silently giving wrong answers on certain boards while passing others. - Forgetting to skip
'.'cells โ adding the dot character to every row, column, and box set looks harmless until any board with two empty cells in the same row returnsfalse. - Checking only rows and columns โ a board can satisfy the row and column constraints while having duplicate digits inside a 3ร3 box; the box check is the constraint developers forget most often.
- Using a single set with string keys like
"row:0:5"โ this works but requires a strict separator;"row:1:2"and"row:12"collide without one, and the subtle bug only surfaces on boards with two-digit-adjacent row/col indices. - Inserting before checking โ if you add the digit first and then test whether the set grew, you lose the short-circuit and can't distinguish a fresh insertion from a duplicate.
Related Problems
sudoku-solverโ extend validation to backtracking-based board completionset-matrix-zeroesโ track and apply a constraint across all rows and columns of a 2D matrixword-searchโ grid traversal with per-cell constraint enforcementcontains-duplicateโ fundamental duplicate detection using a hash setvalid-anagramโ character frequency validation using counting or sets