【算法数据结构】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)
相关推荐
伊玛目的门徒6 小时前
试用leetcode之典中典 二数之和问题
java·算法·leetcode
Jerry7 小时前
LeetCode 226. 翻转二叉树
算法
想做小南娘,发现自己是女生喵8 小时前
【无标题】
数据结构·算法
Kx_Triumphs9 小时前
HDU4348 To the moon(主席树区间修改模板)
算法·题解
旖-旎10 小时前
《LeetCode647 回文子串 || LeetCode 5 最长回文子串》
c++·算法·leetcode·动态规划·哈希算法
轻颂呀11 小时前
约瑟夫环问题
算法
凤凰院凶涛QAQ12 小时前
《Java版数据结构 & 集合类剖析》栈与队列:“push/pop 是栈的灵魂,offer/poll 是队列的骨架——四组 API,两种人生”
java·开发语言·数据结构
科技大视界12 小时前
投资AI项目,传统尽调不够用了——李章虎律师拆解算法、数据、算力三大雷区
人工智能·算法·数据挖掘
郝学胜-神的一滴13 小时前
算法实战:最小k个数——大顶堆的优雅解法
开发语言·数据结构·c++·python·程序人生·算法·排序算法