You are given a 9 x 9 Sudoku board board. A Sudoku board is valid if the following rules are followed:
1-9 without duplicates.1-9 without duplicates.3 x 3 sub-boxes of the grid must contain the digits 1-9 without duplicates.Return true if the Sudoku board is valid, otherwise return false
Note: A board does not need to be full or be solvable to be valid.
Example 1:
Input: board =
[["1","2",".",".","3",".",".",".","."],
["4",".",".","5",".",".",".",".","."],
[".","9","8",".",".",".",".",".","3"],
["5",".",".",".","6",".",".",".","4"],
[".",".",".","8",".","3",".",".","5"],
["7",".",".",".","2",".",".",".","6"],
[".",".",".",".",".",".","2",".","."],
[".",".",".","4","1","9",".",".","8"],
[".",".",".",".","8",".",".","7","9"]]
Output: trueExample 2:
Input: board =
[["1","2",".",".","3",".",".",".","."],
["4",".",".","5",".",".",".",".","."],
[".","9","1",".",".",".",".",".","3"],
["5",".",".",".","6",".",".",".","4"],
[".",".",".","8",".","3",".",".","5"],
["7",".",".",".","2",".",".",".","6"],
[".",".",".",".",".",".","2",".","."],
[".",".",".","4","1","9",".",".","8"],
[".",".",".",".","8",".",".","7","9"]]
Output: falseExplanation: There are two 1's in the top-left 3x3 sub-box.
Constraints:
board.length == 9board[i].length == 9board[i][j] is a digit 1-9 or '.'.
You should aim for a solution as good or better than O(n^2) time and O(n^2) space, where n is the number of rows in the square grid.
Which data structure would you prefer to use for checking duplicates?
You can use a hash set for every row and column to check duplicates. But how can you efficiently check for the squares?
We can find the index of each square by the equation (row / 3) * 3 + (col / 3). Then we use hash set for O(1) lookups while inserting the number into its row, column and square it belongs to. We use separate hash maps for rows, columns, and squares.
Before attempting this problem, you should be comfortable with:
A valid Sudoku board must follow three rules:
1–9 at most once.1–9 at most once.1–9 at most once.We can directly check all these conditions one by one.
For every row, every column, and every 3×3 box, we keep a set of seen digits and make sure no number appears twice.
If we ever find a duplicate in any of these three checks, the board is invalid.
Check all rows:
row from 0 to 8:seen.i from 0 to 8:".".seen, return false.seen.Check all columns:
col from 0 to 8:seen.i from 0 to 8:".".seen, return false.seen.Check all 3×3 boxes:
0 to 8.square:seen.i in 0..2 and j in 0..2:row = (square // 3) * 3 + icol = (square % 3) * 3 + j".".seen, return false.seen.If all rows, columns, and 3×3 boxes pass these checks without duplicates, return true.
class Solution:
def isValidSudoku(self, board: List[List[str]]) -> bool:
for row in range(9):
seen = set()
for i in range(9):
if board[row][i] == ".":
continue
if board[row][i] in seen:
return False
seen.add(board[row][i])
for col in range(9):
seen = set()
for i in range(9):
if board[i][col] == ".":
continue
if board[i][col] in seen:
return False
seen.add(board[i][col])
for square in range(9):
seen = set()
for i in range(3):
for j in range(3):
row = (square//3) * 3 + i
col = (square % 3) * 3 + j
if board[row][col] == ".":
continue
if board[row][col] in seen:
return False
seen.add(board[row][col])
return TrueInstead of checking rows, columns, and 3×3 boxes separately, we can validate the entire Sudoku board in one single pass.
For each cell, we check whether the digit has already appeared in:
We track these using three hash sets:
rows[r] keeps digits seen in row rcols[c] keeps digits seen in column csquares[(r // 3, c // 3)] keeps digits in the 3×3 boxIf a digit appears again in any of these places, the board is invalid.
Create three hash maps of sets:
rows to track digits in each rowcols to track digits in each columnsquares to track digits in each 3×3 sub-box, keyed by (r // 3, c // 3)Loop through every cell in the board:
".".val be the digit in the cell.val is already in:rows[r] → duplicate in the rowcols[c] → duplicate in the columnsquares[(r // 3, c // 3)] → duplicate in the 3×3 boxfalse.Otherwise, add the digit to all three sets:
rows[r]cols[c]squares[(r // 3, c // 3)]If the whole board is scanned without detecting duplicates, return true.
class Solution:
def isValidSudoku(self, board: List[List[str]]) -> bool:
cols = defaultdict(set)
rows = defaultdict(set)
squares = defaultdict(set)
for r in range(9):
for c in range(9):
if board[r][c] == ".":
continue
if ( board[r][c] in rows[r]
or board[r][c] in cols[c]
or board[r][c] in squares[(r // 3, c // 3)]):
return False
cols[c].add(board[r][c])
rows[r].add(board[r][c])
squares[(r // 3, c // 3)].add(board[r][c])
return TrueEvery digit from 1 to 9 can be represented using a single bit in an integer.
For example, digit 1 uses bit 0, digit 2 uses bit 1, …, digit 9 uses bit 8.
This means we can track which digits have appeared in a row, column, or 3×3 box using just one integer per row/column/box instead of a hash set.
When we encounter a digit, we compute its bit position and check:
If none of these checks fail, we “turn on” that bit to mark the digit as seen.
This approach is both memory efficient and fast.
Create three arrays of size 9:
rows[i] stores bits for digits seen in row icols[i] stores bits for digits seen in column isquares[i] stores bits for digits seen in 3×3 box iLoop through each cell (r, c) of the board:
".".val = int(board[r][c]) - 1.mask = 1 << val.Check for duplicates:
mask is already set in rows[r], return false.mask is already set in cols[c], return false.mask is already set in squares[(r // 3) * 3 + (c // 3)], return false.Mark the digit as seen:
rows[r] |= maskcols[c] |= masksquares[(r // 3) * 3 + (c // 3)] |= maskIf all cells are processed without conflicts, return true.
class Solution:
def isValidSudoku(self, board: List[List[str]]) -> bool:
rows = [0] * 9
cols = [0] * 9
squares = [0] * 9
for r in range(9):
for c in range(9):
if board[r][c] == ".":
continue
val = int(board[r][c]) - 1
if (1 << val) & rows[r]:
return False
if (1 << val) & cols[c]:
return False
if (1 << val) & squares[(r // 3) * 3 + (c // 3)]:
return False
rows[r] |= (1 << val)
cols[c] |= (1 << val)
squares[(r // 3) * 3 + (c // 3)] |= (1 << val)
return TrueThe 3x3 box index formula (r // 3) * 3 + (c // 3) is easy to get wrong. A common mistake is using (r // 3, c // 3) as a tuple key but forgetting integer division, or computing r // 3 + c // 3 which doesn't uniquely identify boxes.
# Wrong: doesn't uniquely identify boxes
box_idx = r // 3 + c // 3 # Box (0,1) and (1,0) both give 1
# Correct: unique box index 0-8
box_idx = (r // 3) * 3 + c // 3Empty cells are represented by "." and should be skipped entirely. Forgetting to check for empty cells before processing will cause errors or incorrect duplicate detection.
This problem only checks if the current board state is valid, not whether the puzzle is solvable. A board with no duplicates is valid even if it's impossible to complete.
When iterating through the board, make sure each cell is only processed once. Some implementations accidentally check the same digit multiple times when validating rows, columns, and boxes separately.
Sign in to join the discussion