判断一个 9x9 的数独是否有效。只需要根据以下规则,验证已经填入的数字是否有效即可。
- 数字
1-9
在每一行只能出现一次。 - 数字
1-9
在每一列只能出现一次。 - 数字
1-9
在每一个以粗实线分隔的3x3
宫内只能出现一次。
上图是一个部分填充的有效的数独。
数独部分空格内已填入了数字,空白格用 '.'
表示。
示例 1:
输入: [ ["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"] ] 输出: true
示例 2:
输入: [ ["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"] ] 输出: false 解释: 除了第一行的第一个数字从 5 改为 8 以外,空格内其他数字均与 示例1 相同。 但由于位于左上角的 3x3 宫内有两个 8 存在, 因此这个数独是无效的。
说明:
- 一个有效的数独(部分已被填充)不一定是可解的。
- 只需要根据以上规则,验证已经填入的数字是否有效即可。
- 给定数独序列只包含数字
1-9
和字符'.'
。 - 给定数独永远是
9x9
形式的。
解法一:直接分条判断。复杂度高。
class Solution:
def isValidSudoku(self, board: List[List[str]]) -> bool:
# for i in range(9):
# new_list1 = []
# new_list2 = []
# for j in range(9):
# if board[i][j] != '.' and board[i][j] in new_list1: # 条件一
# return False
# if board[i][j] != '.' and board[i][j] not in new_list1:
# new_list1.append(board[i][j])
# if board[j][i] != '.' and board[j][i] in new_list2: # 条件二
# return False
# if board[j][i] != '.' and board[j][i] not in new_list2:
# new_list2.append(board[j][i])
# 简化,将条件一的每一行的值去重,直接比较长度
for i in range(9):
if 9-len(set(board[i])) != board[i].count(".")-1: # 条件一
return False
new_list = []
for j in range(9): # 条件二
if board[j][i] != "." and board[j][i] in new_list:
return False
if board[j][i] != "." and board[j][i] not in new_list:
new_list.append(board[j][i])
aa, bb = [0,3,6],[0,3,6] # 条件三
for a in aa:
for b in bb:
new_list = []
for i in range(a,a+3):
for j in range(b,b+3):
if board[i][j] != '.' and board[i][j] in new_list:
return False
if board[i][j] != '.' and board[i][j] not in new_list:
new_list.append(board[i][j])
return True
解法二:位图法,利用空间换时间复杂度,循环一次存储,判断。
class Solution:
def isValidSudoku(self, board: List[List[str]]) -> bool:
# 将数独拆开行列组,并放在对应字典中。如第5行,放在dic_row对应下标为4的字典中。
dic_row = [{},{},{},{},{},{},{},{},{}]
dic_col = [{},{},{},{},{},{},{},{},{}]
dic_cel = [{},{},{},{},{},{},{},{},{}]
for i in range(len(board)):
for j in range(len(board)):
# 遍历一次数独,判断是否重复。
num = board[i][j]
if num == ".":
continue
if num not in dic_row[i] and num not in dic_col[j] and num not in dic_cel[3*(i//3)+(j//3)]:
dic_row[i][num] = 1
dic_col[j][num] = 1
dic_cel[3*(i//3)+(j//3)][num] = 1 # 利用地板除将数独分为9组。
else:
return False
return True
解法三: 解法二的另一种实现方式。代码更为简洁(借鉴)
转自:https://blog.csdn.net/huhehaotechangsha/article/details/80538675
Cell = [[] for i in range(9)] # 没有必要用dict,我们只某个数字关心有没有出现过
Col = [[] for i in range(9)]
Row = [[] for i in range(9)]
for i,row in enumerate(board): # 将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列,同时列出数据和数据下标,一般用在 for 循环当中
for j,num in enumerate(row):
if num != '.':
k = (i//3)*3 + j//3
if num in Row[i] + Col[j] + Cell[k]: # list的骚操作,将三个list顺序的拼接
return False
Row[i].append(num)
Col[j].append(num)
Cell[k].append(num)
return True