IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> 算法_回溯_解数独 -> 正文阅读

[数据结构与算法]算法_回溯_解数独

解数独

leetcode链接

1.解法

这道题是一个棋盘问题,而棋盘问题是一个典型的回溯问题。

首先思考普通人(这里指没有受过专业训练的人)解数独的方法,那就是首先在空白位置假设一个数字,然后在这个假设的基础上去填别的数,如果发现到最后无法填满,则证明假设不成立,然后换一个数继续假设。

上面的思想非常符合回溯的思想,画成图如下所示:
在这里插入图片描述
所以我们现在可以进行回溯三部曲:

  1. 确定函数头

    首先思考函数的参数,因为一般的回溯都是通过每次修改参数(比如让参数+1等等),然后再进行操作,但这道题比较特殊,我们来看上图中取1的例子,我们确定取1的位置是通过从头(即row=0,col=0)遍历行和列的方式来确定在空白处取1,取完1后,再进行下一次操作时,发现我们还是要从头遍历行和列才能确定下一个空白的位置,因此这里的参数为空, 即不需要任何参数,因为每一次的操作都是从头开始,不需要在上一层递归的基础上确定某些参数。

    有些人可能会问:第二次找空白值的时候,不是可以从上一次递归的列(col)+1开始吗?这样我们的参数就可以加上col了。

    答:因为下一次找空白值的时候有可能是在下一列,也有可能在下一行,我们还要写一些if语句来进行逻辑判断,然后确定下一层递归时参数是row+1,还是col+1,因此不如每一次都从头开始遍历,这样代码会简洁一些,并且不会影响时间复杂度。

     def backtracking():
    
  2. 确定递归出口:

    递归出口是所有的空都被填完了的时候,那么什么时候就可以知道所有的空都填完了呢,即row==len(board)。

     if row == len(board):
     	return
    

    但是这里有一个问题,就是函数头是没有row参数的,我们写这个出口会出问题,这里可以先看下面,下面会给出解释。

  3. 确定逻辑部分:

    逻辑部分就是每次从头遍历行和列,找到下一个空白的位置,然后从1到9逐次填入空白位置,然后进行下一层的递归,只要找到一个合法答案,就可以退出整个程序了(因为数独只有一个答案)

     for row in range(len(board)): # 遍历行
         for col in range(len(board[0])): # 遍历列
             if board[row][col]!='.': # 找到下一个空白位置
                 continue
             for i in range(1,10): # 给空白位置填上数字
                 if isValid(row,col,str(i),board): # 如果该数字合法,即没有相同数字在同行,同列,同宫
                     board[row][col] = str(i) # 填上
                     if backtracking(): # 递归
                         return True # 这里的return True是给一个信号,证明找到答案了
                     board[row][col] = '.' # 回溯
             return False # 如果执行到这里了,证明[1,9]都没有return True(即该空白位置填[1,9]都不成立),那么就不存在答案了,就可以回溯了
    
     return True # 此时row==len(board),即填满了棋盘
    

    这里其实已经涵盖了上面的递归出口,即最后一行return True,因为如果能执行到最后一行,证明row和col都遍历完最后一个位置了,因此直接return True即可,就不用上面的判断了。

  4. 判断数字是否合法:

     def isValid(row,col,i,board):
         # 检查行
         for c in range(len(board[0])):
             if board[row][c]==i:
                 return False
    
         # 检查列
         for r in range(len(board)):
             if board[r][col]==i:
                 return False
    
         # 检查宫
         rstart = (row // 3) * 3
         cstart = (col // 3) * 3
    
         for r in range(rstart,rstart+3):
             for c in range(cstart,cstart+3):
                 if board[r][c]==i:
                     return False
    
         return True
    

    这里检查是否同列和同行很容易,只要用循环判断同行同列是否存在重复数字即可。难点在于如何判断同宫。

    给定一个位置[row][col],想判断同宫里面是否有相同数字需要如下步骤:

    1. 首先给定一个位置[row,col],需要它所在的宫
    2. 知道了它所在的宫,再找到该宫的比较有代表性的位置(比如第一位,最后一位,最中间一位,这里的代表性指的是确定了这个位置,那么该宫的其他位置也都能确定)

    首先画出图:
    在这里插入图片描述

    1.首先找到位置[row,col]和宫的联系:

    我们先从左到右,从上到下给每个宫按行和列编号(蓝色字体),发现如果每个位置的(row//3)就是对应的宫的行数,(col//3)就是对应的宫的列数。

    2.然后找到宫和有代表性位置的联系:

    发现如果宫按照行和列编号,那么行和列都*3,就变成了该宫的第一个元素的位置

    所以代码中写到,rstart = (row // 3) * 3,cstart = (col // 3) * 3,找到了第一个位置,我们只要遍历行和列后+3的范围内的元素中是否有相同元素即可。

整体代码:

def solveSudoku(board):
    def backtracking():

        for row in range(len(board)):
            for col in range(len(board[0])):
                if board[row][col]!='.':
                    continue
                for i in range(1,10):
                    if isValid(row,col,str(i),board):
                        board[row][col] = str(i)
                        if backtracking():
                            return True
                        board[row][col] = '.'
                return False

        return True # 此时row==len(board),即填满了棋盘

    def isValid(row,col,i,board):
        # 检查行
        for c in range(9):
            if board[row][c]==i:
                return False

        # 检查列
        for r in range(9):
            if board[r][col]==i:
                return False

        # 检查宫
        rstart = (row // 3) * 3
        cstart = (col // 3) * 3

        for r in range(rstart,rstart+3):
            for c in range(cstart,cstart+3):
                if board[r][c]==i:
                    return False

        return True

    backtracking()

2.总结

python

string 转 int : i = int(s)
int 转 string : s = str(i)

算法

回溯算法中确定参数的方法:

找到递归时需要在上一层递归的基础上进行操作的参数。

例如:行,列等等

def backtracking(row)

backtracking(row+1)
  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2022-02-26 11:55:08  更:2022-02-26 11:59:07 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 16:27:57-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码