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.
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 row cols to track digits in each column squares 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 row cols[c] → duplicate in the column squares[(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 True