Problem statement
Naive solution
The simplest way to solve this problem is as follows:
- For each row in the Sudoku board, iterate over the cells in the row. Then, for each cell in the row, iterate over all other 8 cells in the row to ensure that there are no duplicates.
- Repeat step 1 for all 9 columns.
- Repeat step 1 for all 9 $3 \times 3$ sub-boxes.
Although this approach is simple, it is inefficient. Specifically, if there are $n$ rows and columns on the Sudoku board, then step 1 will require $n(n-1) = n^2 - n$ iterations for each row, and since there are $n$ rows, the total number of iterations will be $n^2(n-1) = n^3 - n^2$, which translates to $O(n^3)$ time. A similar analysis can be done for the columns and sub-boxes.
Better solution
In the aforementioned solution, we iterate over the rows, columns, and sub-boxes without attempting to “remember” which numbers we already iterated over. This seems wasteful.
Hence, a more efficient approach to solving this problem would be as follows:
- Initialize three dictonaries:
rows,cols, andboxes.
a. These dictionaries will be used to recall which numbers we already iterated over in the rows, columns, and sub-boxes, respectively.
b. Specifically,rows[i]andcols[i]will each contain a hash set that contains all the unique elements of the(i+1)th row and column, respectively, in the Sudoku board.
c. Similarly,boxes[(i, j)]will contain a hash set that contains the unique elements in the sub-box located in the(i+1)th row and(j+1)th column in the3 x 3grid of sub-boxes.
d. These hash sets will be used to check for duplicates. - For each cell in the Sudoku board:
a. Check if the cell contains “.”. If it doees, skip to the next cell.
b. If the cell is in the first row, check if the value in the cell is inrows[0]. If it is, returnFalse. Otherwise, add the value in the cell torows[0]to remember it for later.
c. Repeat step 2(b) for rows 2-9.
d. If the cell is in the first column, check if the value in the cell is incols[0]. If it is, returnFalse. Otherwise, add the value in the cell tocols[0]to remember it for later.
e. Repeat step 2(d) for columns 2-9.
f. If the cell is in the(0, 0)sub-box, check if the value in the cell is inboxes[(0, 0)]. If it is, returnFalse. Otherwise, add the value in the cell toboxes[(0, 0)]to remember it for later.
g. Repeat step 2(f) for sub-boxes(0, 1), (0, 2), (1, 0), ..., (2, 2).\ - Return
True, which means that no duplicates were found.
This can be naively implemented in Python as follows:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
from collections import defaultdict
class Solution:
def isValidSudoku(self, board: List[List[str]]) -> bool:
rows = defaultdict(set)
cols = defaultdict(set)
boxes = defaultdict(set)
for r in range(len(board)):
for c in range(len(board[0])):
if board[r][c] == ".":
continue
if r == 0:
if board[0][c] in rows[0]:
return False
else:
rows[0].add(board[r][c])
if r == 1:
if board[1][c] in rows[1]:
return False
else:
rows[1].add(board[r][c])
...
if c == 0:
if board[r][0] in cols[0]:
return False
else:
cols[0].add(board[r][c])
if c == 1:
if board[r][1] in cols[1]:
return False
else:
cols[1].add(board[r][c])
...
if r // 3 == 0 and c // 3 == 0:
if board[r][c] in boxes[(0, 0)]:
return False
else:
boxes[(0, 0)].add(board[r][c])
if r // 3 == 0 and c // 3 == 1:
if board[r][c] in boxes[(0, 1)]:
return False
else:
boxes[(0, 1)].add(board[r][c])
if r // 3 == 0 and c // 3 == 2:
if board[r][c] in boxes[(0, 2)]:
return False
else:
boxes[(0, 2)].add(board[r][c])
if r // 3 == 1 and c // 3 == 0:
if board[r][c] in boxes[(1, 0)]:
return False
else:
boxes[(1, 0)].add(board[r][c])
...
return True
However, this code contains redundancies and can be optimized further to be:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from collections import defaultdict
class Solution:
def isValidSudoku(self, board: List[List[str]]) -> bool:
rows = defaultdict(set)
cols = defaultdict(set)
boxes = defaultdict(set)
for r in range(len(board)):
for c in range(len(board[0])):
if board[r][c] == ".":
continue
if board[r][c] in rows[r]:
return False
else:
rows[r].add(board[r][c])
if board[r][c] in cols[c]:
return False
else:
cols[c].add(board[r][c])
if board[r][c] in boxes[(r // 3, c // 3)]:
return False
else:
boxes[(r // 3, c // 3)].add(board[r][c])
return True
If the Sudoku board contains n rows and columns, then this solution runs in $O(n^2)$
time and requires $O(n^2)$ space.