【算法数据结构】leetcode37 解数独


37. 解数独 - 力扣(LeetCode)

题目描述:

题目要求每一行 ,每一列,每个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不用

步骤

  1. 预处理三个数组row、col、box,记录每个行、列、子框中已经存在的数字。

  2. 收集所有需要填写的空格的位置,存入一个列表spaces。

  3. 编写一个回溯函数,参数是当前处理到spaces中的第pos个位置。当pos等于spaces的长度时,说明已经填完所有空格,返回True。

  4. 对于当前处理的空格位置(i,j),尝试填入1到9中的一个数字d。如果该数字满足rowid、coljd、boxkd都为False,那么将该数字填入,并更新这三个数组的状态。然后递归处理pos+1的位置。如果递归返回True,说明后续的填写都成功,那么当前的选择是正确的,直接返回True。如果递归返回False,则说明后续无法完成,需要回溯,恢复三个数组的状态,并将boardij恢复为'.',然后尝试下一个数字。

  5. 如果所有数字都尝试过且都不行,则返回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)
相关推荐
To_OC4 小时前
LC 1 两数之和:面试第一道必考题,暴力解法直接被面试官 pass
javascript·算法·leetcode
鱼鱼不愚与9 小时前
《原来如此 | 第01期:为什么导航软件能预测红绿灯倒计时?》
算法
复杂网络13 小时前
论最小 Agent 计算机的形态
算法
kisshyshy1 天前
🍦 雪糕、食堂、火车厢:三幅漫画吃透栈、队列与链表
javascript·算法
猿人谷1 天前
不只是 CPU 阈值:STAR 如何用 GAT + Transformer 做容器级自动扩缩容?
人工智能·算法
复杂网络2 天前
Stable Diffusion 视觉大模型微调技术深度调研
算法
复杂网络2 天前
基于 Stable Diffusion 架构的视觉大模型代表性工作与原理深度解析
算法
MrZhao4002 天前
Agent Loop 如何用 Hook 扩展:权限、日志与工具拦截
算法
MrZhao4002 天前
Agent 为什么需要 Skills:别把所有知识都塞进 system prompt
算法