The problem
You get a 9×9 Sudoku board as a list of lists of strings. Each cell is a digit '1'–'9' or '.' for empty. Return True if the filled cells break none of the three rules:
- no digit repeats in any row;
- no digit repeats in any column;
- no digit repeats in any of the nine 3×3 boxes.
You don’t have to check that the puzzle can be solved, only that what’s there is consistent.
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
Output: TrueChange the top-left 5 to 8 and the answer becomes False: the top-left box would then hold two 8s.
How to think about it
Start with brute force. Check the rules separately: for each of the 9 rows, collect its digits and look for a repeat; do the same for the 9 columns and the 9 boxes. That’s 27 groups of 9 cells, three passes over the board. It’s correct and already cheap, but it means writing three nearly identical loops, and the box loop is easy to get wrong.
The key insight: every rule is “have I seen this before?” in some group. A digit in cell (r, c) belongs to exactly three groups: row r, column c, and box (r // 3, c // 3). So for each filled cell, build three keys that name the group and the digit, and put them in one set. If any key is already in the set, that digit has appeared twice in that group.
Make the keys impossible to confuse. The code uses (r, 'r', v) for rows, ('c', c, v) for columns and (r // 3, c // 3, v) for boxes. The string sits in a different position in each shape, so a row key can never equal a column or box key. r // 3 and c // 3 map each cell to one of the boxes (0, 0) to (2, 2).
Which pattern is this? Hash set membership with composite keys. The tell is “no duplicates within several overlapping groups”.
Approach
- Create an empty set
seen. - Walk every cell
(r, c). Skip it if it’s'.'. - Build its three keys: row, column and box, each including the digit
v. - If any key is already in
seen, returnFalse. Otherwise add it. - If the walk finishes, return
True.
Solution
def is_valid_sudoku(board):
seen = set()
for r in range(9):
for c in range(9):
v = board[r][c]
if v == '.': continue
keys = [(r, 'r', v), ('c', c, v), (r//3, c//3, v)] # row, column, box
for key in keys:
if key in seen: return False # repeat in that group
seen.add(key)
return True
# is_valid_sudoku(valid_board) -> TrueDry run
The first filled cells of the example board, then the cell where the modified board (top-left changed to 8) fails:
| Cell | v |
Keys added | Result |
|---|---|---|---|
| (0, 0) | 5 | (0,'r','5'), ('c',0,'5'), (0,0,'5') |
new |
| (0, 1) | 3 | (0,'r','3'), ('c',1,'3'), (0,0,'3') |
new |
| (0, 4) | 7 | (0,'r','7'), ('c',4,'7'), (0,1,'7') |
new |
| … | the original board finishes: True | ||
| modified (2, 2) | 8 | (0,0,'8') is already in seen |
False |
In the modified board, (0, 0) added the box key (0, 0, '8'). The 8 at (2, 2) is in the same box, so its box key repeats.
Complexity
- Time: O(1). The board is always 81 cells with 3 keys each. For a general n×n board it’s O(n²).
- Space: O(1). At most 243 keys. O(n²) for the general case.
Edge cases and pitfalls
- Empty board: no keys are ever added, so it returns
True. - Valid ≠ solvable. A board can pass this check and still have no solution. Don’t try to solve it.
- Wrong box index. The box is
(r // 3, c // 3), notr // 3 + c // 3(which collides different boxes) orr % 3. - Ambiguous keys: a key like
(r, v)for rows and(c, v)for columns would collide (row 2 and column 2). Tag each kind of key. - Digits are strings. Compare with
'.', not0orNone.
Related problems
- Contains Duplicate (LeetCode 217): the one-group version of this check.
- N-Queens (LeetCode 51/52): sets of “used” rows, columns and diagonals, used while backtracking.
- Set Matrix Zeroes (LeetCode 73): another grid problem driven by row and column bookkeeping.
- 1.Two Sum (LeetCode 1)
- 2.Valid Anagram (LeetCode 242)
- 3.Contains Duplicate (LeetCode 217)
- 4.Ransom Note (LeetCode 383)
- 5.First Unique Character in a String (LeetCode 387)
- 6.Majority Element (LeetCode 169)
- 7.Isomorphic Strings (LeetCode 205)
- 8.Group Anagrams (LeetCode 49)
- 9.Top K Frequent Elements (LeetCode 347)
- 10.Longest Consecutive Sequence (LeetCode 128)
- 11.Subarray Sum Equals K (LeetCode 560)
- 12.Valid Sudoku (LeetCode 36)
- 13.LRU Cache (LeetCode 146)