LeetCode题练习与总结:解数独

167 篇文章 0 订阅
30 篇文章 0 订阅

一、题目描述

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

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

  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] 是一位数字或者 '.'
  • 题目数据 保证 输入数独仅有一个解

二、解题思路

  1. 选择一个空格:在数独板上找一个还没有填数字的空格。
  2. 尝试填入数字:尝试在该空格中填入1到9之间的数字。
  3. 检查有效性:检查填入的数字是否满足数独的规则。这包括检查当前行、列和3x3的子宫格。
  4. 递归解决:如果填入的数字有效,递归地尝试解决剩余的空格。
  5. 回溯:如果在递归过程中发现当前填入的数字导致无法继续填入其他空格,回溯到上一个空格,尝试填入另一个数字。

三、具体代码

class Solution {
    public void solveSudoku(char[][] board) {
        if (board == null || board.length != 9 || board[0].length != 9) {
            return;
        }

        solve(board, 0, 0); // 从第一个空格开始递归解决
    }

    private boolean solve(char[][] board, int row, int col) {
        if (row == 9) { // 如果到达最后一行,意味着所有空格都已尝试,返回成功
            return true;
        }

        if (col == 9) { // 移动到下一行
            return solve(board, row + 1, 0);
        }

        if (board[row][col] != '.') { // 如果当前格有数字,移动到下一列
            return solve(board, row, col + 1);
        }

        // 尝试填入1到9之间的数字
        for (char num = '1'; num <= '9'; num++) {
            if (isValid(board, row, col, num)) {
                board[row][col] = num; // 填入数字
                if (solve(board, row, col + 1)) { // 递归解决下一个空格
                    return true;
                }
                board[row][col] = '.'; // 回溯,移除数字
            }
        }
        return false; // 如果没有数字可以填入,返回失败
    }

    private boolean isValid(char[][] board, int row, int col, char num) {
        // 检查行
        for (int i = 0; i < 9; i++) {
            if (board[row][i] == num) {
                return false;
            }
        }

        // 检查列
        for (int i = 0; i < 9; i++) {
            if (board[i][col] == num) {
                return false;
            }
        }

        // 检查3x3子宫格
        int startRow = row - row % 3;
        int startCol = col - col % 3;
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
                if (board[startRow + i][startCol + j] == num) {
                    return false;
                }
            }
        }

        return true; // 如果数字不违反规则,返回true
    }
}

四、时间复杂度和空间复杂度

1. 时间复杂度
  • 时间复杂度主要取决于尝试填充数字的次数以及每次尝试时的验证过程。
  • 尝试次数:最坏情况下,每个空格都尝试填入1到9之间的所有数字,因此尝试次数大约是 9^9(因为9x9的数独有81个空格,每个空格最多尝试9次)。
  • 验证过程:每次尝试填入数字时,都需要验证该数字是否有效,这包括检查当前行、列和3x3的宫。最坏情况下,每个验证过程需要检查9个元素,因此对于每次尝试,验证过程的时间复杂度是 O(9)
  • 综上所述,最坏情况下的时间复杂度是 O(9^9 * 9),即 O(9^10)
  • 这是一个非常巨大的数字,但在实际情况中,由于数独的规则限制,通常不需要尝试所有可能的数字组合。
2. 空间复杂度
  • 空间复杂度主要取决于递归调用栈的深度。
  • 递归调用栈:在最坏情况下,每次递归调用都可能需要进入一个新的递归层级,直到找到一个合适的数字或者填满所有空格。由于数独最多有81个空格,递归调用栈的最大深度为81。
  • 因此,空间复杂度是 O(递归调用栈深度),即 O(81)O(1),因为这里的常数可以忽略不计。

五、总结知识点

  1. 回溯算法:这是一种通过递归来实现的深度优先搜索策略。它尝试在给定的问题空间中搜索解决方案,并且在搜索过程中,如果遇到不可行的路径,会返回到上一步,尝试其他可能的选项。

  2. 递归solve 方法是一个递归函数,它尝试填入数字并递归地调用自身来解决剩余的空格。递归的基本情况是当所有空格都被填满时,这时会返回 true,表示找到了一个解决方案。

  3. 参数传递solve 方法通过 boardrowcol 三个参数来跟踪当前的数独板状态和下一个需要填充的空格位置。

  4. 边界检查:在 solve 方法的开始,有对 rowcol 的边界检查,确保它们不会超出数独板的范围。

  5. 条件判断:代码中有多个条件判断,用于确定当前位置是否可以填入某个数字。这包括检查当前行、列和3x3的宫格内是否已经存在该数字。

  6. 有效性验证isValid 方法用于验证尝试填入的数字是否符合数独的规则。它检查了三个维度:行、列和3x3的宫格。

  7. 回溯:当尝试填入的数字导致冲突时,通过将当前位置的值重置为 '.'(表示空格),算法会回溯到上一步,尝试其他可能的数字。

  8. 字符操作:代码中使用了字符型变量 num 来表示数字1到9,这是为了与数独板上的字符表示保持一致。

  9. 数学计算:在计算3x3宫格的起始行和列时,使用了取模运算 % 来确定当前位置所在的宫格。

  10. 空格处理:代码中对数独板上的空格('.')进行了特殊处理,只有当当前位置是空格时,才会尝试填入数字。

以上就是解决这个问题的详细步骤,希望能够为各位提供启发和帮助。

  • 38
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一直学习永不止步

谢谢您的鼓励,我会再接再厉的!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值