问题描述:
请编写一个程序,通过填充空单元格来解决数独问题。一个数独的解法需遵循如下规则:
(1)数字 1-9 在每一行只能出现一次。
(2)数字 1-9 在每一列只能出现一次。
(3)数字 1-9 在每一个以粗实线分隔的 3x3 宫格内只能出现一次。空白格用 '.' 表示。
图1. 左边一个数独,右边答案(答案被标记成红色)
注:
给定的数独序列只包含数字 1-9 和字符 '.' 。
你可以假设给定的数独只有唯一解。
给定数独永远是 9x9 形式。
问题分析:(回溯法)
经典题目,采用回溯法,递归实现(实现简单,不如直接用栈高效),基本思路,首先找到一个待填写的小方格,9个数字依次试探填入,并行下一个方格,如果当前方格9个数字都不满足上面说的三个规则,那么恢复当前环境,并且回溯到前一个方格,依次进行,直至结束。有点像深度优先搜索哦。
Python3实现:
# 回溯法,递归实现
# @Time :2018/6/20
# @Author :Yinxing
class Solution:
def solveSudoku(self, board):
self.board = board
self.solve()
def solve(self): # 主递归函数
row, col = self.findUnassigned() # 获取一个未被分配的方格
if row == -1 and col == -1: # 没有找到,说明已经填好
return True
for num in ['1', '2', '3', '4', '5', '6', '7', '8', '9']:
if self.isSafe(row, col, num): # 循环填入数字,并判断是否满足要求
self.board[row][col] = num
if self.solve(): # 递归进入下一个方格
return True
self.board[row][col] = '.' # 后续不满足,那么现在要回复当前环境,并进行下一个数字试探
return False
def findUnassigned(self): # 依次查找未被分配的方格
for row in range(9):
for col in range(9):
if self.board[row][col] == '.':
return row, col
return -1, -1
def isSafe(self, row, col, ch): # 判断是否当前方格填入的数字是否满足要求
boxrow = row - row % 3 # 确定3x3小宫格的开始坐标,就是3x3小宫格第一个方格索引
boxcol = col - col % 3
if self.checkrow(row, ch) and self.checkcol(col, ch) and self.checksquare(boxrow, boxcol, ch):
return True
return False
def checkrow(self, row, ch): # 检查一行是否符合条件
for col in range(9):
if self.board[row][col] == ch:
return False
return True
def checkcol(self, col, ch): # 检查一列是否符合条件
for row in range(9):
if self.board[row][col] == ch:
return False
return True
def checksquare(self, row, col, ch): # 检查3x3小宫格是否符合条件
for r in range(row, row + 3):
for c in range(col, col + 3):
if self.board[r][c] == ch:
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']]
solu = Solution()
solu.solveSudoku(board)
for v in board:
print(v)
程序还有很多地方可以优化,例如,在搜索可分配的方格时,每次进行了一次全局搜索,其实可以记录已经填写的方格,然后接着这个记录进行搜索,或者返回上一个。欢迎指正哦