题目描述:

题目要求每一行 ,每一列,每个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。如果该数字满足row[i][d]、col[j][d]、box[k][d]都为False,那么将该数字填入,并更新这三个数组的状态。然后递归处理pos+1的位置。如果递归返回True,说明后续的填写都成功,那么当前的选择是正确的,直接返回True。如果递归返回False,则说明后续无法完成,需要回溯,恢复三个数组的状态,并将board[i][j]恢复为'.',然后尝试下一个数字。
-
如果所有数字都尝试过且都不行,则返回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(board[i][j]) 填充的数字作为下标
row: row[i][d]=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 : col[j][d]=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)
box[k][d] = True # 第k 个箱子的d 是否存在
例如 board[4][3]=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)