题目描述:
编写一个程序,通过填充空格来解决数独问题。
数独的解法需 遵循如下规则:
数字 1-9 在每一行只能出现一次。
数字 1-9 在每一列只能出现一次。
数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。(请参考示例图)
数独部分空格内已填入了数字,空白格用 ‘.’ 表示。
示例:
输入: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”]]
输出:[[“5”,“3”,“4”,“6”,“7”,“8”,“9”,“1”,“2”],[“6”,“7”,“2”,“1”,“9”,“5”,“3”,“4”,“8”],[“1”,“9”,“8”,“3”,“4”,“2”,“5”,“6”,“7”],[“8”,“5”,“9”,“7”,“6”,“1”,“4”,“2”,“3”],[“4”,“2”,“6”,“8”,“5”,“3”,“7”,“9”,“1”],[“7”,“1”,“3”,“9”,“2”,“4”,“8”,“5”,“6”],[“9”,“6”,“1”,“5”,“3”,“7”,“2”,“8”,“4”],[“2”,“8”,“7”,“4”,“1”,“9”,“6”,“3”,“5”],[“3”,“4”,“5”,“2”,“8”,“6”,“1”,“7”,“9”]]
解释:输入的数独如上图所示,唯一有效的解决方案如下所示:
提示:
board.length == 9
board[i].length == 9
board[i][j] 是一位数字或者 ‘.’
题目数据 保证 输入数独仅有一个解
思路:
按照「行优先」的顺序依次枚举每一个空白格中填的数字,通过**递归 + 回溯(剪枝)**的方法枚举所有可能的填法。当递归到最后一个空白格后,如果仍然没有冲突,说明我们找到了答案;在递归的过程中,如果当前的空白格不能填下任何一个数字,那么就进行回溯。
class Solution(object):
def solveSudoku(self, board):
"""
:type board: List[List[str]]
:rtype: None Do not return anything, modify board in-place instead.
"""
self.result = []
self.slove(board)
return self.result
def slove(self, board):
flag = True
for i_c in range(9):
for j_c in range(9):
if board[i_c][j_c] == '.':
flag = False
if flag:
self.result = board
return True
f = False
for i in range(9):
for j in range(9):
if board[i][j] == '.':
a = i
b = j
f = True
break
if f:
break
# 测试1到9是否能填进去
for k in range(1, 10):
if self.isValid(board, a, b, k):
board[a][b] = str(k)
if self.slove(board):
return True
else:
board[a][b] = '.'
def isValid(self, board, a, b, k):
# 检查行是否有一样的
for h in range(9):
if board[a][h] == str(k):
return False
# 检查列是否有一样的:
for m in range(9):
if board[m][b] == str(k):
return False
# 检查所在的小方块是否满足:
g_a = int(a / 3)
g_b = int(b / 3)
set_g = set()
for g_i in range(3):
for g_j in range(3):
if board[g_i+g_a*3][g_j+g_b*3] != '.':
if board[g_i+g_a*3][g_j+g_b*3] not in set_g:
set_g.add(board[g_i+g_a*3][g_j+g_b*3])
if str(k) in set_g:
return False
return True
这里需要注意的是,同之前写剪枝的问题(N皇后)一样,在使用递归函数的时候,最好参数要重新生成一个这样避免结果一直被inplace的修改,或者inplace一个结果保存下来也是可以;但是在本问题中,因为要求的rtype中是要inplace的取结果且结果只有一个,所以这里比较有技巧的把递归函数的返回值定义成一个bool的类型,找到一个结果就结束了,不需要把所有的可能都试一遍,这样保证board就是结果。如果题目中,不要求是是inplace的到返回值,也可以对返回的结果进行拷贝(深拷贝)得到结果,同样的要是数独的解不是一个的时候,就需要每次深拷贝结果或者每次调用递归函数的时候换一块空间。
import copy
class Solution(object):
def solveSudoku(self, board):
"""
:type board: List[List[str]]
:rtype: None Do not return anything, modify board in-place instead.
"""
# 暴力填写,从第一个不为空的地方开始
self.result = []
self.slove(board)
return self.result
def slove(self, board):
flag = True
for i_c in range(9):
for j_c in range(9):
if board[i_c][j_c] == '.':
flag = False
if flag:
# print("找到解")
self.result = copy.deepcopy(board)
# print(board)
return True
f = False
for i in range(9):
for j in range(9):
if board[i][j] == '.':
a = i
b = j
f = True
break
if f:
break
# 测试1到9是否能填进去
# print("a,b:", a, b)
for k in range(1, 10):
if self.isValid(board, a, b, k):
board[a][b] = str(k)
self.slove(board)
board[a][b] = '.'
def isValid(self, board, a, b, k):
# 检查行是否有一样的
for h in range(9):
if board[a][h] == str(k):
return False
# 检查列是否有一样的:
for m in range(9):
if board[m][b] == str(k):
return False
# 检查所在的小方块是否满足:
g_a = int(a / 3)
g_b = int(b / 3)
set_g = set()
for g_i in range(3):
for g_j in range(3):
if board[g_i+g_a*3][g_j+g_b*3] != '.':
if board[g_i+g_a*3][g_j+g_b*3] not in set_g:
set_g.add(board[g_i+g_a*3][g_j+g_b*3])
if str(k) in set_g:
return False
return True
if __name__ == '__main__':
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"]]
# board = [['5', '3', '4', '6', '7', '8', '9', '1', '2'], ['6', '7', '2', '1', '9', '5', '8', '3', '4'],
# ['1', '9', '8', '3', '4', '2', '5', '6', '7'], ['8', '5', '9', '7', '6', '1', '4', '2', '3'],
# ['4', '2', '6', '8', '5', '3', '7', '9', '1'], ['7', '1', '3', '9', '2', '4', '.', '.', '6'],
# ['.', '6', '.', '.', '.', '.', '2', '8', '.'], ['.', '.', '.', '4', '1', '9', '.', '.', '5'],
# ['.', '.', '.', '.', '8', '.', '.', '7', '9']]
a = Solution()
print(a.solveSudoku(board))