from typing import List
from collections import defaultdict
Problem Description
Difficulty: Medium
You are given a a 9 x 9 Sudoku board board. A Sudoku board is valid if the following rules are followed:
- Each row must contain the digits 1-9 without duplicates.
- Each column must contain the digits 1-9 without duplicates.
- Each of the nine 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.
Examples are provided in the code below.
Constraints:
board.length == 9
board[i].length == 9
board[i][j] is a digit 1-9 or '.'.
Initial Solution
Oh, man. This will involve a few loops and a lot of conditional statements. There is probably a neat mathematical trick but I’d rather not spend the time trying to figure it out. Let’s brute force the problem…
= [["1","2",".",".","3",".",".",".","."],
board1 "4",".",".","5",".",".",".",".","."],
[".","9","8",".",".",".",".",".","3"],
["5",".",".",".","6",".",".",".","4"],
[".",".",".","8",".","3",".",".","5"],
["7",".",".",".","2",".",".",".","6"],
[".",".",".",".",".",".","2",".","."],
[".",".",".","4","1","9",".",".","8"],
[".",".",".",".","8",".",".","7","9"]]
[
# Output: True
= [["1","2",".",".","3",".",".",".","."],
board2 "4",".",".","5",".",".",".",".","."],
[".","9","1",".",".",".",".",".","3"],
["5",".",".",".","6",".",".",".","4"],
[".",".",".","8",".","3",".",".","5"],
["7",".",".",".","2",".",".",".","6"],
[".",".",".",".",".",".","2",".","."],
[".",".",".","4","1","9",".",".","8"],
[".",".",".",".","8",".",".","7","9"]]
[
# Output: False (two 1's in the top left 3x3 box)
= [["1","2",".",".","3",".",".",".","."],
board3 "4",".",".","5",".",".",".",".","."],
[".","9","1",".",".",".",".",".","3"],
["5",".","1",".","6",".",".",".","4"],
[".",".",".","8",".","3",".",".","5"],
["7",".",".",".","2",".",".",".","6"],
[".",".",".",".",".",".","2",".","."],
[".",".",".","4","1","9",".",".","8"],
[".",".",".",".","8",".",".","7","9"]]
[
# Output: False (two 1's in column 2)
class Solution:
def isValidSudoku(self, board: List[List[str]]) -> bool:
= defaultdict(list)
row_dict = defaultdict(list)
columns = defaultdict(list)
box
for i, row in enumerate(board):
for j, value in enumerate(row):
# ignore dots
if value == '.':
continue
# check if seen in a row before
if value in row_dict[i]: return False
row_dict[i].append(value)
# create columns and check if valid
if value in columns[j]: return False
columns[j].append(value)
# create 3x3 boxes and check if valid
if i < 3:
if j < 3:
if value in box[0]: return False
0].append(value)
box[elif j < 6:
if value in box[1]: return False
1].append(value)
box[else:
if value in box[2]: return False
2].append(value)
box[elif i < 6:
if j < 3:
if value in box[3]: return False
3].append(value)
box[elif j < 6:
if value in box[4]: return False
4].append(value)
box[else:
if value in box[5]: return False
5].append(value)
box[else:
if j < 3:
if value in box[6]: return False
6].append(value)
box[elif j < 6:
if value in box[7]: return False
7].append(value)
box[else:
if value in box[8]: return False
8].append(value)
box[return True
= [board1, board2, board3]
boards = [True, False, False]
expected = Solution()
solution
# Get results
= [solution.isValidSudoku(board) for board in boards]
results
# Compare results with expected values
if results == expected:
print("Tests passed")
else:
print("Tests failed")
print("Expected:", expected)
print("Got:", results)
Tests passed
Summary
- Time Complexity: \(O(1)\) as the operations are bounded by a fixed number of iterations and checks in a 9x9 Sudoku board (specifically, 81 operations). If the size of the board were variable, the complexity would be \(O(n^2)\) due to the nested loop.
- Space Complexity: \(O(1)\) as the space used by the defaultdicts is also bounded by the fixed number of entries in the 9x9 board (specifically, 3 dictionaries each holding up to 81 entries in total).
NeetCode Solution
class Solution:
def isValidSudoku(self, board: List[List[str]]) -> bool:
= defaultdict(set)
cols = defaultdict(set)
rows = defaultdict(set) # key = (r /3, c /3)
squares
for r in range(9):
for c in range(9):
if board[r][c] == ".":
continue
if (
in rows[r]
board[r][c] 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])// 3, c // 3)].add(board[r][c])
squares[(r
return True
Conclusion
NeetCode’s solution is a lot cleaner but is essentially the same approach. However, it does use a neat trick of integer division to compute the 3x3 grids. It also uses defaultdict(set)
instead defaultdict(list)
which will improve the efficiency of the lookup operations.