Skip to content

Valid Sudoku

Determine if a 9 × 9 Sudoku board is valid. Only the filled cells need to be validated according to the following rules:

  • Each row must contain the digits 1–9 without repetition.
  • Each column must contain the digits 1–9 without repetition.
  • Each of the nine 3×3 sub-boxes of the grid must contain the digits 1–9 without repetition.

Note: A Sudoku board (partially filled) could be valid but is not necessarily solvable. Only the filled cells need to be validated.

BoardValid?Reason
Standard partially filled valid boardNo duplicates in any row, column, or box
Board with '8' repeated in the same rowDuplicate 8 in row 0

Valid board (first 3 rows shown):

["5","3",".",".","7",".",".",".","."]
["6",".",".","1","9","5",".",".","."]
[".","9","8",".",".",".",".","6","."]

No digit repeats in any row, column, or 3×3 box — valid.

Invalid board:

["8","8",".",".","7",".",".",".","."]
...

'8' appears twice in row 0 — invalid.

  • board.length == 9
  • board[i].length == 9
  • board[i][j] is a digit '1''9' or '.'
ApproachTimeSpaceBest When
Hash SetsO(1)O(1)Board is always 9×9 — both are constant
BitmaskO(1)O(1)Micro-optimisation, avoids hash overhead

Both are O(1) since the board size is fixed at 9×9.

  • Pick Hash Sets for interviews — it reads naturally and is easy to explain.
  • Pick Bitmask as a follow-up to show you can eliminate hash overhead with integer operations.
Most Readable
Hash Sets
O(1) time · O(1) space
Fastest in Practice
Bitmask
O(1) time · O(1) space
★ Recommended

Use three arrays of 9 sets — rows[9], cols[9], boxes[9] — to track which digits have been seen. For every non-empty cell (r, c) with digit d:

  1. Compute box = (r // 3) * 3 + (c // 3)
  2. If d is already in rows[r], cols[c], or boxes[box] → return false
  3. Otherwise add d to all three sets

If no conflict is found after all 81 cells → return true.

⏱ Time O(1) 81 cells fixed 💾 Space O(1) 27 sets of ≤9 digits

The 9 boxes are numbered 0–8 left-to-right, top-to-bottom:

rows \ cols0–23–56–8
0–2box 0box 1box 2
3–5box 3box 4box 5
6–8box 6box 7box 8

Formula: box = (row // 3) * 3 + (col // 3)

Sample row 0: ["5","3",".",".","7",".",".",".","."]

Step(r, c)dboxrows[0] aftercols[c] afterboxes[box] after
1(0, 0)50{5}{5}{5}
2(0, 1)30{5,3}{3}{5,3}
3(0, 2).skipskipskip
4(0, 3).skipskipskip
5(0, 4)71{5,3,7}{7}{7}
.skipskipskip

Animated walkthrough

Hash sets tracking row 0 of a valid Sudoku board

Use Next or Autoplay to see each cell visited and its digit added to the row, column, and box sets.

Step 1 of 6
Waiting to begin...
Array state
Memory / lookup state

function isValidSudoku(board):
rows = 9 empty sets
cols = 9 empty sets
boxes = 9 empty sets
for r in 0..8:
for c in 0..8:
d = board[r][c]
if d == '.': continue
box = (r // 3) * 3 + (c // 3)
if d in rows[r] or d in cols[c] or d in boxes[box]:
return false
add d to rows[r], cols[c], boxes[box]
return true
valid_sudoku_hash_sets.py
from typing import List
from collections import defaultdict
def isValidSudoku(board: List[List[str]]) -> bool:
rows = defaultdict(set)
cols = defaultdict(set)
boxes = defaultdict(set)
for r in range(9):
for c in range(9):
d = board[r][c]
if d == '.':
continue
box = (r // 3) * 3 + (c // 3)
if d in rows[r] or d in cols[c] or d in boxes[box]:
return False
rows[r].add(d)
cols[c].add(d)
boxes[box].add(d)
return True
🎯 Interview Favourite

Replace each set with a single integer. Bit k (0-indexed) is set when digit k+1 has been seen. This eliminates hash overhead and reduces each check to a bitwise AND.

  • Check digit d: (mask >> (d - 1)) & 1 == 1
  • Set digit d: mask |= (1 << (d - 1))
⏱ Time O(1) 81 cells fixed 💾 Space O(1) 27 integers

For digit 5: bit index = 5 - 1 = 4, so bit pattern = 0b000010000 (bit 4 set).

Digit seenBit setmask value
5bit 40b000010000 = 16
3bit 20b000010100 = 20
7bit 60b001010100 = 84

To check if 3 is already in this mask: 84 & (1 << 2) = 84 & 4 = 4 ≠ 0 → duplicate detected.

function isValidSudoku(board):
rows = [0] * 9
cols = [0] * 9
boxes = [0] * 9
for r in 0..8:
for c in 0..8:
d = board[r][c]
if d == '.': continue
bit = 1 << (int(d) - 1)
box = (r // 3) * 3 + (c // 3)
if rows[r] & bit or cols[c] & bit or boxes[box] & bit:
return false
rows[r] |= bit
cols[c] |= bit
boxes[box] |= bit
return true
valid_sudoku_bitmask.py
from typing import List
def isValidSudoku(board: List[List[str]]) -> bool:
rows = [0] * 9
cols = [0] * 9
boxes = [0] * 9
for r in range(9):
for c in range(9):
d = board[r][c]
if d == '.':
continue
bit = 1 << (int(d) - 1)
box = (r // 3) * 3 + (c // 3)
if rows[r] & bit or cols[c] & bit or boxes[box] & bit:
return False
rows[r] |= bit
cols[c] |= bit
boxes[box] |= bit
return True
✓ Simple

A third approach demonstrating an alternative technique for solving this problem. This implementation optimizes either time or space complexity compared to the previous approaches.

⏱ Time O(n) Optimized complexity 💾 Space O(1) Efficient space usage
function approach3():
// Implementation approach goes here
valid_sudoku_space_optimized.py
"""
Solution for Valid Sudoku
"""
def solve():
"""Implementation for approach 3 of Valid Sudoku"""
pass
if __name__ == "__main__":
print("Solution ready")