【算法数据结构】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)
相关推荐
程序员老舅3 小时前
啃透 I2C 驱动开发,才算入门嵌入式 Linux 内核驱动
数据结构·驱动开发·b树·内核·嵌入式·嵌入式开发·i2c
lueluelue473 小时前
LeetCode:链表
算法·leetcode·链表
橘子汽水1686 小时前
Leetcode 23,543合并K个升序链表,二叉树的直径
算法·leetcode·链表
huameinan狮子6 小时前
Adaboost算法原理与计算实例
算法
别怪我很水6 小时前
API中提供了VelocityTracker类用于计算触摸事件MotionEvent的速度,而其内部默认使用的方法就是最小二乘法,本 ...
算法·gitee·最小二乘法
Re.不晚9 小时前
挑战做100道力扣算法- DAY1
算法·leetcode·职场和发展
蓝悦无人机10 小时前
《Planning algorithms》读书笔记——第1章 引言
算法·读书笔记·规划算法·lavalle
Sagittarius_A*10 小时前
分组密码基础(二):Feistel 结构与 DES 的设计思想
算法·信息安全·密码学·des·数论
青山木10 小时前
Hot 100 --- 搜索插入位置
java·数据结构·算法·leetcode