当前位置: 首页 > news >正文

【LeetCode 题解】算法:36.有效的数独

一、问题剖析

在算法领域中,数独问题是一个经典且有趣的逻辑验证题目。本题的核心任务是判断一个给定的 9x9 数独是否有效。判断的依据是数独的基本规则:数字 1-9 在每一行、每一列以及每一个 3x3 的宫内都只能出现一次。同时,题目中明确指出,空白格用 '.' 表示,并且一个有效的数独(部分已被填充)不一定是可解的,我们只需验证已填入数字的有效性即可。

二、解题思路

  1. 行的验证:遍历数独的每一行,使用一个布尔数组来记录数字 1-9 是否出现过。在遍历每一行的过程中,对于每个数字,如果该数字已经在当前行出现过(即布尔数组对应位置为 true),则说明该行无效,直接返回 false;如果该数字为 '.',则跳过继续遍历;如果遍历完一行,没有出现重复数字,则该行有效。

  2. 列的验证:类似于行的验证,遍历数独的每一列。同样使用一个布尔数组来记录数字 1-9 是否出现过。在遍历每一列的过程中,对于每个数字,检查其是否已经在当前列出现过,若出现重复则返回 false;遇到 '.' 则跳过;遍历完一列没有重复数字则该列有效。

  3. 3x3 宫的验证:将数独划分为 9 个 3x3 的宫,遍历每个宫。对于每个宫,使用一个布尔数组来记录数字 1-9 是否出现过。通过计算每个宫的起始位置,然后遍历宫内的每个数字,检查是否出现重复,若出现重复则返回 false;遇到 '.' 则跳过;遍历完一个宫没有重复数字则该宫有效。

  4. 整体判断:当行、列、宫都验证通过后,说明这个数独是有效的,返回 true;只要有一个部分验证不通过,就返回 false。

三、Java 代码实现

​
public class ValidSudoku {
    public boolean isValidSudoku(char[][] board) {
        // 验证每一行
        for (int i = 0; i < 9; i++) {
            boolean[] row = new boolean[9];
            for (int j = 0; j < 9; j++) {
                if (board[i][j] != '.') {
                    int num = board[i][j] - '1';
                    if (row[num]) {
                        return false;
                    }
                    row[num] = true;
                }
            }
        }

        // 验证每一列
        for (int j = 0; j < 9; j++) {
            boolean[] col = new boolean[9];
            for (int i = 0; i < 9; i++) {
                if (board[i][j] != '.') {
                    int num = board[i][j] - '1';
                    if (col[num]) {
                        return false;
                    }
                    col[num] = true;
                }
            }
        }

        // 验证 3x3 宫
        for (int startRow = 0; startRow < 9; startRow += 3) {
            for (int startCol = 0; startCol < 9; startCol += 3) {
                boolean[] box = new boolean[9];
                for (int i = startRow; i < startRow + 3; i++) {
                    for (int j = startCol; j < startCol + 3; j++) {
                        if (board[i][j] != '.') {
                            int num = board[i][j] - '1';
                            if (box[num]) {
                                return false;
                            }
                            box[num] = true;
                        }
                    }
                }
            }
        }

        return true;
    }
}

​

四、代码详细解析

1、行的验证代码

// 验证每一行
for (int i = 0; i < 9; i++) {
    boolean[] row = new boolean[9];
    for (int j = 0; j < 9; j++) {
        if (board[i][j] != '.') {
            int num = board[i][j] - '1';
            if (row[num]) {
                return false;
            }
            row[num] = true;
        }
    }
}

外层循环 for (int i = 0; i < 9; i++) 遍历数独的每一行。每次进入循环,创建一个长度为 9 的布尔数组 row 用于记录当前行数字 1-9 的出现情况。内层循环 for (int j = 0; j < 9; j++) 遍历当前行的每一个数字。如果当前数字不是 '.',将其转换为数组索引(通过 board[i][j] - '1'),检查该索引对应的布尔值是否为 true,若为 true 则说明数字重复,返回 false;若为 false,则将其设置为 true 表示该数字已出现。

2、列的验证代码

// 验证每一列
for (int j = 0; j < 9; j++) {
    boolean[] col = new boolean[9];
    for (int i = 0; i < 9; i++) {
        if (board[i][j] != '.') {
            int num = board[i][j] - '1';
            if (col[num]) {
                return false;
            }
            col[num] = true;
        }
    }
}

外层循环 for (int j = 0; j < 9; j++) 遍历数独的每一列。每次进入循环,创建一个长度为 9 的布尔数组 col 用于记录当前列数字 1-9 的出现情况。内层循环 for (int i = 0; i < 9; i++) 遍历当前列的每一个数字。逻辑与行的验证类似,检查当前数字是否重复,若重复则返回 false。

3、3x3 宫的验证代码

// 验证 3x3 宫
for (int startRow = 0; startRow < 9; startRow += 3) {
    for (int startCol = 0; startCol < 9; startCol += 3) {
        boolean[] box = new boolean[9];
        for (int i = startRow; i < startRow + 3; i++) {
            for (int j = startCol; j < startCol + 3; j++) {
                if (board[i][j] != '.') {
                    int num = board[i][j] - '1';
                    if (box[num]) {
                        return false;
                    }
                    box[num] = true;
                }
            }
        }
    }
}

外层的两个循环 for (int startRow = 0; startRow < 9; startRow += 3) 和 for (int startCol = 0; startCol < 9; startCol += 3) 用于遍历 9 个 3x3 的宫。每次进入循环,创建一个长度为 9 的布尔数组 box 用于记录当前宫数字 1-9 的出现情况。内层的两个循环 for (int i = startRow; i < startRow + 3; i++) 和 for (int j = startCol; j < startCol + 3; j++) 遍历当前宫的每一个数字,检查是否重复,若重复则返回 false。

4、最终返回结果

return true;

当行、列、宫都验证通过后,说明数独有效,返回 true。

五、复杂度分析

  1. 时间复杂度:整个算法需要遍历数独的每一行、每一列以及每一个 3x3 的宫。数独的大小为 9x9,对于行的验证,需要遍历 9 行,每行 9 个数字,时间复杂度为 (O(9times9));列的验证同理,时间复杂度也为 (O(9times9));3x3 宫的验证,需要遍历 9 个宫,每个宫有 9 个数字,时间复杂度为 (O(9times9))。所以总的时间复杂度为 (O(9times9 + 9times9 + 9times9) = O(243) = O(1)),因为 9 是固定的常数。

  2. 空间复杂度:在验证过程中,我们使用了布尔数组来记录数字的出现情况。对于每一行、每一列以及每一个 3x3 的宫,都创建了一个长度为 9 的布尔数组,总共需要的空间为 (9 + 9 + 9 = 27),也是一个固定的常数,所以空间复杂度为 (O(1))。

六、测试用例验证

1、测试用例 1

public class Main {
    public static void main(String[] args) {
        ValidSudoku solution = new ValidSudoku();
        char[][] board1 = {
            {'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'}
        };
        System.out.println(solution.isValidSudoku(board1)); // 输出: true
    }
}

2、测试用例 2

public class Main {
    public static void main(String[] args) {
        ValidSudoku solution = new ValidSudoku();
        char[][] board2 = {
            {'8', '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'}
        };
        System.out.println(solution.isValidSudoku(board2)); // 输出: false
    }
}

感谢各位的阅读,后续将持续给大家讲解力扣中的算法题和数据库题,如果觉得这篇内容对你有帮助,别忘了点赞和关注,后续还有更多精彩的算法解析与你分享!

相关文章:

  • C++20新特性:协程
  • 一文解析2025年移动游戏用户获取策略 | 增长方法论
  • 【CF】Day30——Codeforces Round 824 (Div. 2) C + Codeforces Round 825 (Div. 2) BC1
  • 数字内容体验的核心价值是什么?
  • 【前后端】npm包mysql2的使用__nodejs mysql包升级版
  • 四、TorchRec的推理优化
  • RAG(检索增强生成)系统,提示词(Prompt)表现测试(数据说话)
  • 如何在AMD MI300X 服务器上部署 DeepSeek R1模型?
  • c++关键字new
  • CExercise_07_1指针和数组_2数组元素的逆序数组逆序(指针版 reverse_by_ptr 和下标版 reverse_arr)
  • NVIDIA H100 vs A100:新一代GPU架构性能对比分析
  • 第五章:5.1 ESP32物联网应用 - MQTT协议深度教程
  • 大厂文章阅读
  • 速盾:高防CDN节点对收录有影响吗?
  • 深入解析 Microcom:嵌入式串口调试利器
  • 代码随想录算法训练营Day27 | Leetcode 56. 合并区间、738.单调递增的数字、968.监控二叉树
  • C++中的设计模式
  • Linux中的Vim与Nano编辑器命令详解
  • 第8课:多智能体系统评估与迭代
  • 【模板】缩点
  • 2017 如何做网站优化/如何将网站的关键词排名优化
  • 网站设计建设公司/深圳网站优化软件
  • 阿里巴巴b2b商业模式分析/seo文章
  • 企业安全文化建设方案/吉安seo
  • 30个让人兴奋的视差滚动网站/成都新站软件快速排名
  • linux tomcat 网站目录/百度seo在哪里