20191030-带返回值的回溯算法Leetcode解数独

题目描述

编写一个程序,通过已填充的空格来解决数独问题。

一个数独的解法需遵循如下规则:

数字 1-9 在每一行只能出现一次。

数字 1-9 在每一列只能出现一次。

数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。

空白格用 ‘.‘ 表示。

一个数独。

答案被标成红色。

Note:

给定的数独序列只包含数字 1-9 和字符 ‘.‘ 。

你可以假设给定的数独只有唯一解。

给定数独永远是 9x9 形式的。

输入格式:

[["5","3",".",".","7",".",".",".","."],["6",".",".","1","9","5",".",".","."],[".","9","8",".",".",".",".","6","."],["8",".",".",".","6",".",".",".","3"],["4",".",".","8",".","3",".",".","1"],["7",".",".",".","2",".",".",".","6"],[".","6",".",".",".",".","2","8","."],[".",".",".","4","1","9",".",".","5"],[".",".",".",".","8",".",".","7","9"]]

输出格式:

[[‘5‘, ‘3‘, ‘4‘, ‘6‘, ‘7‘, ‘8‘, ‘9‘, ‘1‘, ‘2‘], [‘6‘, ‘7‘, ‘2‘, ‘1‘, ‘9‘, ‘5‘, ‘3‘, ‘4‘, ‘8‘], [‘1‘, ‘9‘, ‘8‘, ‘3‘, ‘4‘, ‘2‘, ‘5‘, ‘6‘, ‘7‘], [‘8‘, ‘5‘, ‘9‘, ‘7‘, ‘6‘, ‘1‘, ‘4‘, ‘2‘, ‘3‘], [‘4‘, ‘2‘, ‘6‘, ‘8‘, ‘5‘, ‘3‘, ‘7‘, ‘9‘, ‘1‘], [‘7‘, ‘1‘, ‘3‘, ‘9‘, ‘2‘, ‘4‘, ‘8‘, ‘5‘, ‘6‘], [‘9‘, ‘6‘, ‘1‘, ‘5‘, ‘3‘, ‘7‘, ‘2‘, ‘8‘, ‘4‘], [‘2‘, ‘8‘, ‘7‘, ‘4‘, ‘1‘, ‘9‘, ‘6‘, ‘3‘, ‘5‘], [‘3‘, ‘4‘, ‘5‘, ‘2‘, ‘8‘, ‘6‘, ‘1‘, ‘7‘, ‘9‘]]

难度分类

困难

算法分析


row


col


Box

这道题的的思考是在9x9的格子里面找到未填充的数字,比如当找到第n个未填充数字的格子,此时尝试在里面填写1可以保证当前行不冲突,列不冲突,小方块不冲突,那么就在第n个格子里面填写1,然后查看第n+1个格子,如果第n+1个格子发现填写1-9都冲突,说明第n个格子填写1的情况下无法找到解,此时我们需要回到第n个格子,填写另外一个不冲突的数字。一直到能填充到第81个格子为止。

将上述解题思路转换为代码设计:

  1. 定义col_used存储对应列的值,col_used = [[[] for i in range(9)]]
  2. 定义row_used存储对应行的值,row_used=[[] for i in range(9)]
  3. 定义box_used存储对应小方块的值,box_used = [[[], [], []] for i in range(3)]
  4. 先遍历九宫格将已填充的数字记录进对应的行,列和小方块
  5. 通过定义i,j,遍历遍历九宫格,i代表行的index,j代表列的index,遍历九宫格
  6. 读取当前九宫格的值,当当前九宫格的值为“”.“”的时候进行以下操作:

a)     从1-9九个数字中选择一个数字(满足与当前行的数字不冲突,列的数字不冲突,小方块的数字不冲突)填入当前格子

b)     将这个填入的数字记录进对应的行,对应的列,对应的小方块

c)     判断在当前格子填写当前数字的基础上接下来的格子填充能否填满九宫格

d)     如果当前格子填写数字的基础上接下来的格子填充不能填满九宫格,则需要取消当前数字的填充即①取消已填充的数字,②将已填充的数字从对应的行取出,③将已填充的数字从对应的列取出,④将已填充的数字从对应的小方块取出。然后尝试填入其他数字

e)     通过True和False判断能否填写九宫格,仅当第81个格子填写完成(填写的过程中已经进行了条件校验)才返回True,否则返回False

  1. 如果当前格子已有数字直接查看下一个格子

要点:如果下层返回False,证明基于当前数字的填充没有解,重置九宫格,行,列,小方块状态,选取新的数字填充

代码示例

def solveSudoku(board):
    # 记录某一列已使用的数字
    row_used = [[] for i in range(9)]
    # 记录某一个小方块已使用的数字
    col_used = [[] for i in range(9)]
    # 记录某一行已使用的数字
    box_used = [[[], [], []] for i in range(3)]
    # 首先记录九宫格中每行,每列,每一个小方块中已使用的数字,以用于填充数字的时候检查填充数字是否已被使用
    for i in range(len(board)):
        for j in range(len(board[i])):
            if board[i][j] in "123456789":
                row_used[i].append(board[i][j])
                col_used[j].append(board[i][j])
                box_used[i // 3][j // 3].append(board[i][j])
    def dfs(board, i=0, j=0):
        # 从左到右一个一个格子的遍历九宫格,当某一行遍历结束遍历下一行的第一个格子
        if j == 9:
            i,j = i+1,0
        if i == 9:
            return True
        # 当遍历到某一行为".",即需要填充数字的时候,此时需要从1-9九个数字中选择一个不冲突的数字填入当前格子
        if board[i][j] ==".":
            for num in ("123456789"):
                # 填充格子的前提条件是行不冲突(每一行每一个数字仅出现一次),列不冲突(每一列每一个数字仅出现一次),小方块不冲突(每一小方块每一个数字仅出现一次)
                if num not in row_used[i] and num not in col_used[j] and num not  in box_used[i // 3][j // 3]:
                    # 填充格子的同时需要将当前数字计入对应的行,列,小方块,用于当其他格子填充的时候检查是否行,列,小方块冲突
                    row_used[i].append(num)
                    col_used[j].append(num)
                    box_used[i // 3][j // 3].append(num)
                    # 填充当前格子
                    board[i][j] = num
                    # 填充完成格子后此时检查基于当前格子已经填充了数字的基础上,填充下一个格子
                    if dfs(board,i,j+1):
                        return True
                    # 如果下一个格子填充失败,1-9九个数字都有冲突,证明当前格子填充当前数字不能找到有效的解,此时需要找到另外一个数字来填充当前格子,因此需要将当前格子恢复原始状态
                    # 恢复当前格子为未填充状态
                    row_used[i].pop()
                    col_used[j].pop()
                    box_used[i // 3][j // 3].pop()
                    board[i][j] = "."
            return False
        # 当遍历到某一格子为数字,直接查看下一个格子
        else:
            return dfs(board, i,j+1)
    dfs(board)
board = [["5","3",".",".","7",".",".",".","."],["6",".",".","1","9","5",".",".","."],[".","9","8",".",".",".",".","6","."],["8",".",".",".","6",".",".",".","3"],["4",".",".","8",".","3",".",".","1"],["7",".",".",".","2",".",".",".","6"],[".","6",".",".",".",".","2","8","."],[".",".",".","4","1","9",".",".","5"],[".",".",".",".","8",".",".","7","9"]]
solveSudoku(board)
print(board)

考点分析

  1. 矩阵的遍历
  2. 带返回值的回溯用法

原文地址:https://www.cnblogs.com/hyj691001/p/11768149.html

时间: 2024-10-13 20:11:36

20191030-带返回值的回溯算法Leetcode解数独的相关文章

Java 中带参带返回值方法的使用

如果方法既包含参数,又带有返回值,称为带参带返回值的方法. 例: 实现功能:将考试成绩排序并输出,返回成绩的个数 实现思路: 1. 定义一个包含整型数组参数的方法,用来传入成绩数组 2. 在方法体中使用 Arrays.sort( ) 方法对成绩数组进行排序,然后使用 Arrays.toString( ) 方法将数组转换为字符串并输出,最后使用 return 返回数组中元素的个数. 3. 调用方法时需要先创建对象,然后再调用.调用时为其传入成绩数组,并获取方法的返回值,保存在变量中,最后输出成绩的

Java 中无参带返回值方法的使用

如果方法不包含参数,但有返回值,我们称为无参带返回值的方法. 例如:下面的代码,定义了一个方法名为 calSum ,无参数,但返回值为 int 类型的方法,执行的操作为计算两数之和,并返回结果 在 calSum( ) 方法中,返回值类型为 int 类型,因此在方法体中必须使用 return 返回一个整数值. 调用带返回值的方法时需要注意,由于方法执行后会返回一个结果,因此在调用带返回值方法时一般都会接收其返回值并进行处理.如: 运行结果为: 两数之和为:17 不容忽视的“小陷阱”: 1. 如果方

Android带返回值的窗口跳转

1.AndroidManifest.xml <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.fish.helloworld" android:versionCode="1" android:vers

Java基础---Java中无参数带返回值方法的使用(三十七)

Java 中无参带返回值方法的使用 如果方法不包含参数,但有返回值,我们称为无参带返回值的方法. 例如:下面的代码,定义了一个方法名为 calSum ,无参数,但返回值为 int 类型的方法,执行的操作为计算两数之和,并返回结果 在 calSum( ) 方法中,返回值类型为 int 类型,因此在方法体中必须使用 return 返回一个整数值. 调用带返回值的方法时需要注意,由于方法执行后会返回一个结果,因此在调用带返回值方法时一般都会接收其返回值并进行处理.如: 运行结果: 不容忽视的"小陷阱&

ajax请求且带返回值的代码实例

ajax请求且带返回值的代码实例: 现在比较流行使用jquery的ajax来实现一些无刷新请求效果,本章节提供一个非常简单的代码实例供大家参考之用,希望能够给需要的朋友带来一定的帮助,代码如下: <script type="text/javascript"> /* 请求Ajax 带返回值,并弹出提示框提醒 --------------------------------------------------*/ function getAjax(url,parm,callBa

13 继续C#中的方法,带返回值的方法介绍

在这一个练习中,我们要使用带返回值的方法.如果一个方法带返回值,那么它的形式是这样的. 定义一个带返回值的C#方法 static 返回类型 方法名字 (参数类型 参数1的名字,参数类型 参数2的名字) { 你的代码 return 返回值 } static: 先不用管是什么意思.在没有讲它的含义之前,在你写的每个方法中都要加上它. 返回类型:就是我们前面讲过的数据类型,int,string等等.通常一个方法调用后要给调用这个方法的地方返回一个值. 方法名字:你可以给方法起一个你想要的名字.不能瞎起

EF5中 执行 sql语句使用Database.ExecuteSqlCommand 返回影响的行数 ; EF5执行sql查询语句 Database.SqlQuery 带返回值

一: 执行sql语句,返回受影响的行数 在mysql里面,如果没有影响,那么返回行数为  -1 ,sqlserver 里面  还没有测试过 using (var ctx = new MyDbContext()) { ctx.Database.ExecuteSqlCommand("UPDATE Person SET Name = 'Michael' WHERE PersonID = 1"); } 二 : Database.SqlQuery<T>   EF5执行sql查询语句

测试 多线程 实现 callable 带返回值

1 package threadTest; 2 3 import java.util.ArrayList; 4 import java.util.Date; 5 import java.util.concurrent.Callable; 6 import java.util.concurrent.ExecutionException; 7 import java.util.concurrent.ExecutorService; 8 import java.util.concurrent.Exec

慕课网-Java入门第一季-7-3 Java 中无参带返回值方法的使用

来源:http://www.imooc.com/code/1579 如果方法不包含参数,但有返回值,我们称为无参带返回值的方法. 例如:下面的代码,定义了一个方法名为 calSum ,无参数,但返回值为 int 类型的方法,执行的操作为计算两数之和,并返回结果 在 calSum( ) 方法中,返回值类型为 int 类型,因此在方法体中必须使用 return 返回一个整数值. 调用带返回值的方法时需要注意,由于方法执行后会返回一个结果,因此在调用带返回值方法时一般都会接收其返回值并进行处理.如: