题目描述:

题目要求每一行 ,每一列,每个3*3 的子框只能出现一次。每个格子的数字范围1-9.
需要遍历每个空格填入可能的数字,并验证符合规则。如果符合就填入,不符合就回溯。
解法:
回溯算法通常用于解决这种需要试错的问题。当发现当前路径无解时候,返回上一步重新选择。
需要遍历每个空格,找到需要填写的空格,对每个空格尝试填入1-9,检查这个数字是否满足行,列 ,子框的要求。如果满足就填入这个数字,递归进行下一个空格,如果后续处理中,发现无法填入下去,就需要回溯。恢复这个空格的状态,尝试下一个可能的数字。
检查某个数字是否可以填入当前位置
对于 位置(i,i)需要检查i 行,j 列,所在的子框是否有这个数字,属于第几个子框可以用 (i/3 )*3,(j/3 )*3来确定 。i=4 4/3=1 1*3=3 ,起始行是3 ,遍历这个3*3的子框。
对于每个空格最多检查1-9个数字。,每个数字需要检查三个方向,最坏情况可能出现重复检查。
优化方法
用哈希表或者数据记录行,列,子框。已经出现的数字。用3个二维数组,记录rowi 表示第i行是否存在数组num,colj 表示第j 列是否存在数据num,boxk 表示第k 个子框中是否存咋这个数字num,
k 可以通过 i//3*3 +j//3 来计算
i=0 ,j=0 ,k=0
i=3,j=3 ,k=4 在第四个子框。
预处理这三个数组,遍历整个数独,对这个对每个非空的格子,将对应的行列,子框中盖数字标记为存在。比如broad_i 为5 ,那么需要row_i 设置为true ,col_j 设置为true ,box_k (k为子框的索引)设置为true。
对每个空格尝试填入1-9 数字中的一个,
初始化 9X10 数组 其中索引 0不用
步骤
-
预处理三个数组row、col、box,记录每个行、列、子框中已经存在的数字。
-
收集所有需要填写的空格的位置,存入一个列表spaces。
-
编写一个回溯函数,参数是当前处理到spaces中的第pos个位置。当pos等于spaces的长度时,说明已经填完所有空格,返回True。
-
对于当前处理的空格位置(i,j),尝试填入1到9中的一个数字d。如果该数字满足rowid、coljd、boxkd都为False,那么将该数字填入,并更新这三个数组的状态。然后递归处理pos+1的位置。如果递归返回True,说明后续的填写都成功,那么当前的选择是正确的,直接返回True。如果递归返回False,则说明后续无法完成,需要回溯,恢复三个数组的状态,并将boardij恢复为'.',然后尝试下一个数字。
-
如果所有数字都尝试过且都不行,则返回False,触发上一层递归的回溯。
这样,当递归函数返回True时,说明已经找到解,此时board已经被正确填充。
# 初始化 3个数组
row = [[False] * 10 for i in range(9)]
print(row) # 每行十个元素
col = [[False] * 10 for i in range(9)]
box = [[False] * 10 for i in range(9)]
[[False, False, False, False, False, False, False, False, False, False],
[False, False, False, False, False, False, False, False, False, False],
[False, False, False, False, False, False, False, False, False, False],
[False, False, False, False, False, False, False, False, False, False],
[False, False, False, False, False, False, False, False, False, False],
[False, False, False, False, False, False, False, False, False, False],
[False, False, False, False, False, False, False, False, False, False],
[False, False, False, False, False, False, False, False, False, False],
[False, False, False, False, False, False, False, False, False, False]]

遍历数组填充已经有的数字 spaces 用于记录空格的位置
# 遍历整个数组 记录每个行 ,列子框中已经存在的数字
spaces = []
for i in range(9):
for j in range(9):
if board[i][j] != '.':
d = int(board[i][j])
row[i][d] = True
col[j][d] = True
k = (i // 3) * 3 + (j // 3)
box[k][d] = True # 第k 个箱子的d 是否存在
# 保存空格的位置到spaces
else:
spaces.append((i, j))
print("空格的位置", spaces)
空格的位置 (0, 2), (0, 3), (0, 5), (0, 6), (0, 7), (0, 8), (1, 1), (1, 2), (1, 6), (1, 7), (1, 8), (2, 0), (2, 3), (2, 4), (2, 5), (2, 6), (2, 8), (3, 1), (3, 2), (3, 3), (3, 5), (3, 6), (3, 7), (4, 1), (4, 2), (4, 4), (4, 6), (4, 7), (5, 1), (5, 2), (5, 3), (5, 5), (5, 6), (5, 7), (6, 0), (6, 2), (6, 3), (6, 4), (6, 5), (6, 8), (7, 0), (7, 1), (7, 2), (7, 6), (7, 7), (8, 0), (8, 1), (8, 2), (8, 3), (8, 5), (8, 6)
d = int(boardij) 填充的数字作为下标
row: rowid=True
[[False, False, False, True(3), False, True(5), False, True(7), False, False],
[False, True(1), False, False, False, True(5), True(6), False, False, True(9)],
[False, False, False, False, False, False, True(6), False, True(8), True(9)],
[False, False, False, True(3), False, False, True(6), False, True(8), False],
[False, True(1), False, True(3), True(4), False, False, False, True(8), False],
[False, False, True(2), False, False, False, True(6), True(7), False, False],
[False, False, True(2), False, False, False, True(6), False, True(8), False],
[False, True(1), False, False, True(4), True(5)), False, False, False, True(9)],
[False, False, False, False, False, False, False, True(7), True(8), True(9)]]
col : coljd=true 纵坐标作为 行 d作为列
第一列 4 5 6 7 8
False, False, False, False, True(4), True(5), True(6), True(7), True(8), False
[[False, False, False, False, True(4), True(5), True(6), True(7), True(8), False],
[False, False, False, True, False, False, True, False, False, True],
[False, False, False, False, False, False, False, False, True, False],
[False, True, False, False, True, False, False, False, True, False],
[False, True, True, False, False, False, True, True, True, True],
[False, False, False, True, False, True, False, False, False, True],
[False, False, True, False, False, False, False, False, False, False],
[False, False, False, False, False, False, True, True, True, False],
[False, True, False, True, False, True, True, False, False, True]]
box : k = (i // 3) * 3 + (j // 3)
boxkd = True # 第k 个箱子的d 是否存在
例如 board43=8 k=(4//3)*3 +(3//1) =4 第4个子框 第8个标记为true

[[False, False, False, True, False, True, True, False, True, True],
[False, True, False, False, False, True, False, True, False, True],
[False, False, False, False, False, False, True, False, False, False],
[False, False, False, False, True, False, False, True, True, False],
[False, False, True, True, False, False, True, False, True(8), False],
[False, True, False, True, False, False, True, False, False, False],
[False, False, False, False, False, False, True, False, False, False],
[False, True, False, False, True, False, False, False, True, True],
[False, False, True, False, False, True, False, True, True, True]]
定义回溯
# 定义回溯
def backtrack(pos):
print("pos", pos)
if pos == len(spaces): # 当pos 等于spaces长度时候说明已经填写完 ,返回true
print("结果", board)
return True
i, j = spaces[pos]
k = (i // 3) * 3 + (j // 3)
for d in range(1, 10): # 对这个位置尝试填充1-9
if not row[i][d] and not col[j][d] and not box[k][d]: # 满足为false 时候填入
row[i][d] = True
col[j][d] = True
box[k][d] = True
board[i][j] = str(d)
if backtrack(pos + 1):
return True
# 回溯
row[i][d] = False
col[j][d] = False
box[k][d] = False
board[i][j] = '.' # 恢复空格
return False
backtrack(0)
代码
class Solution:
def solveSudoku(self, board: List[List[str]]) -> None:
"""
Do not return anything, modify board in-place instead.
"""
# 初始化三个数组
row=[[False] *10 for _ in range(9)]
col=[[False] *10 for _ in range(9)]
box=[[False] *10 for _ in range(9)]
spaces=[] # 记录空格位置
# 遍历数组填充已经有的数字
for i in range(9):
for j in range(9):
if board[i][j]!='.':
d=int(board[i][j])
row[i][d]=True
col[j][d]=True
k=(i//3)*3+(j//3)
box[k][d]=True
else:
spaces.append((i,j)) # 记录空格的坐标
def backtrack(pos):
if pos ==len(spaces):#遍历完了所有空格
return True
i,j=spaces[pos] # 取出当前位置的空格坐标
k=(i//3)*3+(j//3)
for d in range(1,10): # 尝试对该位置填充1-9
if row[i][d]==False and col[j][d]==False and box[k][d]==False:
row[i][d]=True
col[j][d]=True
box[k][d]=True
board[i][j]=str(d)
if backtrack(pos+1):
return True
#不满足 回溯
row[i][d]=False
col[j][d]=False
box[k][d]=False
board[i][j]='.'
return False
backtrack(0)