判断一个 9*9 的数独面板是否是有效的。
如果已经被填充的数字满足以下条件,则说明是有效的:
- 每一行只能包含无重复数字1-9
- 每一列只能包含无重复数字1-9
- 每一个 3*3 的子面板只能包含无重复数字1-9
备注:
- 一个有效的数独面板(部分填充)不必是可解的
- 只要被填充的数字满足有效条件即可
- 数独面板可以只被部分填充,其他空缺的为字符’.’
- 面板只能包含数字1-9或者字符’.’
- 面板大小总是 9*9
Example 1:
Input:
[
[“5”,“3”,".",".",“7”,".",".",".","."],
[“6”,".",".",“1”,“9”,“5”,".",".","."],
[".",“9”,“8”,".",".",".",".",“6”,"."],
[“8”,".",".",".",“6”,".",".",".",“3”],
[“4”,".",".",“8”,".",“3”,".",".",“1”],
[“7”,".",".",".",“2”,".",".",".",“6”],
[".",“6”,".",".",".",".",“2”,“8”,"."],
[".",".",".",“4”,“1”,“9”,".",".",“5”],
[".",".",".",".",“8”,".",".",“7”,“9”]
]
Output: true
Example 2:
Input:
[
[“8”,“3”,".",".",“7”,".",".",".","."],
[“6”,".",".",“1”,“9”,“5”,".",".","."],
[".",“9”,“8”,".",".",".",".",“6”,"."],
[“8”,".",".",".",“6”,".",".",".",“3”],
[“4”,".",".",“8”,".",“3”,".",".",“1”],
[“7”,".",".",".",“2”,".",".",".",“6”],
[".",“6”,".",".",".",".",“2”,“8”,"."],
[".",".",".",“4”,“1”,“9”,".",".",“5”],
[".",".",".",".",“8”,".",".",“7”,“9”]
]
Output: false
Explanation: Same as Example 1, except with the 5 in the top left corner being
modified to 8. Since there are two 8’s in the top left 3x3 sub-box, it is invalid.
思路
1、定义法
按照数独的有效性定义,需要从行、列、3*3子块三个方面都满足数字的唯一性。
因此,可以分三个步骤,分别检验三个条件,只要有一个条件不满足,则说明数独无效。
1、检验每一行是否有重复数字。可以定义一个set,存放扫描过的数字,一旦新数字已经被包含在set中,则说明重复。
2、检验每一列是否有重复数字。与行同理。
3、检验每一个子块是否有重复数字。需要检查9个子块。
2、一次检验法
上述方法是分别检验三个条件,每个条件都需要遍历一遍所有的数字。
实际上,可以只通过一次遍历,来同时判断是否满足三个条件。
维护一个 set,这个set中存放三种格式的元素:
- (int i, char c):二元组,表示第 i 行中存在 c 字符。
- (char c, int i):二元组,表示第 i 列中存在 c 字符。
- (i, j, c):三元组,表示第 [i, j] 个子块中存在 c 字符,其中 。
由于元组长度以及元素类型的不同,三种元素不会相互混合。
因此,每次扫描到一个数字,便生成 3 个对应的元组,分别判断是否存在于 set 中,如果存在,则说明数独无效;否则,将 3 个元组都加入到 set 中,继续扫描下一个数字。
python实现
def isValidSudoku(board):
"""
:type board: List[List[str]]
:rtype: bool
依次判断3个有效条件是否全部满足。
"""
def is_row_valid(board):
'''
判断一个数独的每一行是否都包含无重复数字1-9
'''
for row in board:
char_set = set()
for char in row:
if char != '.':
if char in char_set:
return False
char_set.add(char)
return True
# 1、每一行一定要包含无重复数字1-9
if not is_row_valid(board):
return False
# 2、每一列一定要包含无重复数字1-9
board_reverse = [[board[i][j] for i in range(9)] for j in range(9)]
if not is_row_valid(board_reverse):
return False
# 3、每一个 3*3 的子面板一定要包含无重复数字1-9
for i in range(3):
for j in range(3):
char_set = set()
for ii in range(i*3, (i+1)*3):
for jj in range(j*3, (j+1)*3):
char = board[ii][jj]
if char != '.':
if char in char_set:
return False
char_set.add(char)
return True
def isValidSudoku2(board):
"""
:type board: List[List[str]]
:rtype: bool
一次遍历,同时判断是否满足3个有效条件。
"""
char_set = set()
for i in range(9):
for j in range(9):
char = board[i][j]
if char != '.':
if (i, char) in char_set \
or (char, j) in char_set \
or (i//3, j//3, char) in char_set:
return False
else:
char_set.add((i, char)) # 第i行的char字符
char_set.add((char, j)) # 第j列的char字符
# [i//3, j//3]位置的小面板的char字符
char_set.add((i//3, j//3, char))
return True
if '__main__' == __name__:
board = [
["5","3",".",".","7",".",".",".","."],
["6",".",".","1","9","5",".",".","."],
[".","9","8",".",".",".",".","6","."],
["8",".",".",".","6",".",".",".","3"],
["4",".",".","8",".","3",".",".","1"],
["7",".",".",".","2",".",".",".","6"],
[".","6",".",".",".",".","2","8","."],
[".",".",".","4","1","9",".",".","5"],
[".",".",".",".","8",".",".","7","9"]
]
print(isValidSudoku2(board))