解数独--难的一批

这篇具有很好参考价值的文章主要介绍了解数独--难的一批。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1题目

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

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

  1. 数字 1-9 在每一行只能出现一次。
  2. 数字 1-9 在每一列只能出现一次。
  3. 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。(请参考示例图)

数独部分空格内已填入了数字,空白格用 '.' 表示。

示例 1:

解数独--难的一批

输入: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"]]
输出:[["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"]]
解释:输入的数独如上图所示,唯一有效的解决方案如下所示:


解数独--难的一批

提示:

  • board.length == 9
  • board[i].length == 9
  • board[i][j] 是一位数字或者 '.'
  • 题目数据 保证 输入数独仅有一个解

2链接

题目链接:37. 解数独 - 力扣(LeetCode)

视频链接:回溯算法二维递归?解数独不过如此!| LeetCode:37. 解数独_哔哩哔哩_bilibili

3解题思路

本题与N皇后问题不同的是,递归维度又高了一层。以前都是一维递归,本题是二维递归

N皇后问题 (opens new window)是因为每一行每一列只放一个皇后,只需要一层for循环遍历一行,递归来遍历列,然后一行一列确定皇后的唯一位置。

本题就不一样了,本题中棋盘的每一个位置都要放一个数字(而N皇后是一行只放一个皇后),并检查数字是否合法,解数独的树形结构要比N皇后更宽更深

因为这个树形结构太大,抽取一部分,如图所示:

解数独--难的一批

回溯三部曲

1、确定函数参数及返回值

递归函数的返回值需要是bool类型,为什么呢?

因为解数独找到一个符合的条件(就在树的叶子节点上)立刻就返回,相当于找从根节点到叶子节点一条唯一路径,所以需要使用bool返回值。

bool backtracking(vector<vector<char>>& board)

2、确定递归终止条件

本题递归不用终止条件,解数独是要遍历整个树形结构寻找可能的叶子节点就立刻返回。

不用终止条件会不会死循环?

递归的下一层的棋盘一定比上一层的棋盘多一个数,等数填满了棋盘自然就终止(填满当然好了,说明找到结果了),所以不需要终止条件!

那么有没有永远填不满的情况呢?

这个问题在递归单层搜索逻辑里再来讲!

3、单层递归逻辑

在树形图中可以看出我们需要的是一个二维的递归(也就是两个for循环嵌套着递归)

一个for循环遍历棋盘的行,一个for循环遍历棋盘的列,一行一列确定下来之后,递归遍历这个位置放9个数字的可能性!

bool backtracking(vector<vector<char>>& board) {
    for (int i = 0; i < board.size(); i++) {        // 遍历行
        for (int j = 0; j < board[0].size(); j++) { // 遍历列
            if (board[i][j] != '.') continue;
            for (char k = '1'; k <= '9'; k++) {     // (i, j) 这个位置放k是否合适
                if (isValid(i, j, k, board)) {
                    board[i][j] = k;                // 放置k
                    if (backtracking(board)) return true; // 如果找到合适一组立刻返回
                    board[i][j] = '.';              // 回溯,撤销k
                }
            }
            return false;                           // 9个数都试完了,都不行,那么就返回false
        }
    }
    return true; // 遍历完没有返回false,说明找到了合适棋盘位置了
}

注意:if (backtracking(board)) return true; // 如果找到合适一组立刻返回。然后就不会再撤销填进去的数字k了,这样就保证了合法的数字k留在了棋盘之中。

注意这里return false的地方,这里放return false 是有讲究的

因为如果一行一列确定下来了,这里尝试了9个数都不行,说明这个棋盘找不到解决数独问题的解!

那么会直接返回, 这也就是为什么没有终止条件也不会永远填不满棋盘而无限递归下去!

4、递归三部曲外番--判断棋盘合法性

判断棋盘是否合法有如下三个维度:文章来源地址https://www.toymoban.com/news/detail-478167.html

  • 同行是否重复
  • 同列是否重复
  • 9宫格里是否重复
bool isValid(int row, int col, char val, vector<vector<char>>& board) {
    for (int i = 0; i < 9; i++) { // 判断行里是否重复
        if (board[row][i] == val) {
            return false;
        }
    }
    for (int j = 0; j < 9; j++) { // 判断列里是否重复
        if (board[j][col] == val) {
            return false;
        }
    }
    int startRow = (row / 3) * 3;
    int startCol = (col / 3) * 3;
    for (int i = startRow; i < startRow + 3; i++) { // 判断9方格里是否重复
        for (int j = startCol; j < startCol + 3; j++) {
            if (board[i][j] == val ) {
                return false;
            }
        }
    }
    return true;
}

4代码

class Solution {
private:
    bool isvalid(int row, int col, char val, vector<vector<char>>& board) {
        for (int i = 0; i < 9; i++) { //判断当前行是否重复
            if (board[row][i] == val) return false;
        }
        for (int j = 0; j < 9; j++) { //判断当前列是否重复
            if (board[j][col] == val) return false;
        }
        int startRow = (row / 3) * 3; //通过去余的方式,重定位每个小九宫格的起始位置
        int startCol = (col / 3) * 3;
        for (int i = startRow; i < startRow + 3; i++) {// 判断9方格里是否重复
            for (int j = startCol; j < startCol + 3; j++) {
                if (board[i][j] == val) return false;
            }
        }
        return true;
    }

    bool backtracking(vector<vector<char>>& board) {
        for (int i = 0; i < board.size(); i++) { // 遍历行
            for (int j = 0; j < board[0].size(); j++) { // 遍历列
                if (board[i][j] == '.') { //遇到空格才继续往下判断是否可行
                    for (char k = '1'; k <= '9'; k++) { // (i, j) 这个位置放k是否合适
                        if (isvalid(i, j, k, board)) { 
                            board[i][j] = k; //放置k
                            if (backtracking(board)) return true; // 如果找到合适一组立刻返回
                            board[i][j] = '.'; //回溯撤销k
                        }                        
                    }
                    return false; //九个数字都实验完了,均找不到一组解
                }
            }
        }
        return true; // 遍历完没有返回false,说明找到了合适棋盘位置了
    }
public:
    void solveSudoku(vector<vector<char>>& board) {
        backtracking(board);
    }
};

到了这里,关于解数独--难的一批的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • python编写一个小程序,python入门小程序编写

    大家好,小编来为大家解答以下问题,python编写一个小程序,python入门小程序编写,现在让我们一起来看看吧! 大家好,小编为大家解答python简单易懂的小程序的问题。很多人还不知道python入门小程序编写,现在让我们一起来看看吧! 20个小段程序 1.字符串翻转 运行结果:

    2024年02月03日
    浏览(43)
  • Linux下获取另外一个程序的标准输出和标准错误输出的一种实现方式

    问题:一个程序如何获取另外一个程序的标准输出和标准错误输出? 标准输入,标准输出,标准错误输出是一个程序的基本组成,在Linux下一个程序调用另外一个程序,如何获取其标准输出和错误输出呢? 分析:一个程序获取另外一个程序的信息,本质上是IPC(基于进程的通

    2024年02月13日
    浏览(41)
  • Java——一个简单的数学题目生成和回答的程序

    这段代码是一个简单的数学题目生成和回答的程序。具体分析如下: 导入必要的类: 代码中导入了用于输入输出的  java.io  包和生成随机数的  java.util.Random  类。这些类将在后面的代码中使用到。 定义主类和主方法: 代码中定义了一个名为  例229  的类,并在其中定义了

    2024年02月11日
    浏览(32)
  • 本题要求编写程序,计算并输出2个正整数的和、差、积、商与余数。题目保证输入和输出全部在整型范围内。

    输入在一行中给出2个正整数A和B。 输出格式: 在5行中按照格式“A 运算符 B = 结果”顺序输出和、差、积、商与余数。 输入样例: 输出样例: 在这里给出相应的输出。例如:

    2024年02月08日
    浏览(58)
  • 微信小程序拍照解数独

    之前刷数独玩,出于偷懒的思想,总想着用计算机去解决。算法没少些,之前通过手工录入的9x9数据解数独,深度优先遍历算法,很容易。但总想更加方便一点,比如拍照直接解析一个数独的表格,自动解出来。 正好前几天休假,有空,就做了一些尝试,然后基本上给做出来

    2024年02月10日
    浏览(35)
  • python烟花代码通过编写程序来模拟烟花的绽放过程

    下面是一个简单的 Python 烟花代码,可以通过编写程序来模拟烟花的绽放过程: 该代码使用 turtle 库来绘制烟花的效果。首先,设置窗口大小和标题,定义烟花的颜色和数量。然后,定义烟花的形状,采用 turtle.Shape 的方式来定义,包括圆形和尾迹。接着,注册烟花的形状,采

    2024年02月05日
    浏览(42)
  • 用python编写一个小程序,如何用python编写软件

    大家好,给大家分享一下用python编写一个小程序,很多人还不知道这一点。下面详细解释一下。现在让我们来看看! 我想有人曲解意思了,人家说用python开发渣蔽一个手机app,不是说用手机敲写python代码,当然可以啊,只不过在电脑上开发的应用软件要进行打包什么的,才能

    2024年02月07日
    浏览(44)
  • 在Python中编写一个翻译程序

    本文使用创作助手。 要在Python中编写一个翻译程序,你可以使用 googletrans 库。以下是一个使用 googletrans 库进行翻译的简单示例: 在上述示例中,你需要将 要翻译的文本 替换为你想要翻译的文本, en 表示目标语言为英语。你可以根据需要指定不同的目标语言代码,如 fr 表

    2024年04月17日
    浏览(44)
  • 编写并调试一个堆栈溢出的程序

    编写存在栈溢出漏洞的 c++ 程序: stack_overflow.cpp 对于 GCC 编译器,可以尝试使用 -fno-stack-protector ​ 选项来禁用堆栈保护。 使用 -g ​使其可调试 编译命令: g++ stack_overflow.cpp -o stack_overflow -g -fno-stack-protector ​ ‍ 使用 gdb 查看 stack_overflow 函数的地址:0x000055555555520e 命令是

    2024年03月26日
    浏览(51)
  • 用python做一个微信小程序,用python编写一个小程序

    这篇文章主要介绍了python制作小程序制作流程,具有一定借鉴价值,需要的朋友可以参考下。希望大家阅读完这篇文章后大有收获,下面让小编带着大家一起了解一下。 大家好,小编为大家解答用python写的好玩的小程序的问题。很多人还不知道python简单的小程序,现在让我们

    2024年04月25日
    浏览(35)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包