LeetCode 第36题:有效的数独

LeetCode 第36题:有效的数独

题目描述

请你判断一个 9 x 9 的数独是否有效。只需要 根据以下规则 ,验证已经填入的数字是否有效即可。

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

注意:

  • 一个有效的数独(部分已填)不一定是可解的。
  • 只需要根据以上规则,验证已经填入的数字是否有效即可。
  • 空白格用 '.' 表示。

难度

中等

题目链接

点击在LeetCode中查看题目

示例

示例 1:

数独示例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"]]
输出:true

示例 2:

输入:board = 
[["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"]]
输出:false
解释:第一行的第一个数字 8 在第四行中重复出现

提示

  • board.length == 9
  • board[i].length == 9
  • board[i][j] 是一个数字 1-9 或者 '.'

解题思路

方法一:哈希表记录

使用三个不同的哈希表分别记录每行、每列、每个3x3方格中数字的出现情况。

关键点:

  1. 使用哈希集合记录每个区域的数字
  2. 巧妙地计算3x3方格的索引
  3. 一次遍历完成所有验证

具体步骤:

  1. 创建三个哈希表数组:
    • rows[9]: 记录每行的数字
    • cols[9]: 记录每列的数字
    • boxes[9]: 记录每个3x3方格的数字
  2. 遍历整个数独板:
    • 计算当前位置属于哪个3x3方格
    • 检查数字在三个区域是否重复
    • 如果重复返回false

时间复杂度:O(1),因为是固定大小的9x9网格
空间复杂度:O(1),使用固定大小的哈希表

方法二:位运算优化

使用位运算来优化空间使用,每个数字用一个位表示。

图解思路

验证过程分析表

步骤 验证区域 检查内容 结果 说明
初始状态 第一行 数字5 有效 第一次出现数字5
继续检查 第一列 数字5 有效 列中第一次出现5
继续检查 第一个3x3宫 数字5 有效 3x3宫中第一次出现5
发现重复 第四行 数字8 无效 与第一行的8重复

数据结构状态表

数据结构 用途 存储内容 优势
HashSet数组 行验证 每行出现的数字 查找O(1)
HashSet数组 列验证 每列出现的数字 空间友好
HashSet数组 3x3宫验证 每个宫内数字 实现简单
位运算数组 全部验证 二进制位表示 性能最优

代码实现

哈希表版本

public class Solution {
    public bool IsValidSudoku(char[][] board) {
        var rows = new HashSet<char>[9];
        var cols = new HashSet<char>[9];
        var boxes = new HashSet<char>[9];
    
        for (int i = 0; i < 9; i++) {
            rows[i] = new HashSet<char>();
            cols[i] = new HashSet<char>();
            boxes[i] = new HashSet<char>();
        }
    
        for (int i = 0; i < 9; i++) {
            for (int j = 0; j < 9; j++) {
                if (board[i][j] != '.') {
                    char num = board[i][j];
                    int boxIndex = (i / 3) * 3 + j / 3;
                
                    if (rows[i].Contains(num) || 
                        cols[j].Contains(num) || 
                        boxes[boxIndex].Contains(num)) {
                        return false;
                    }
                
                    rows[i].Add(num);
                    cols[j].Add(num);
                    boxes[boxIndex].Add(num);
                }
            }
        }
    
        return true;
    }
}

位运算版本

public class Solution {
    public bool IsValidSudoku(char[][] board) {
        int[] rows = new int[9];
        int[] cols = new int[9];
        int[] boxes = new int[9];
    
        for (int i = 0; i < 9; i++) {
            for (int j = 0; j < 9; j++) {
                if (board[i][j] != '.') {
                    int num = board[i][j] - '1';
                    int boxIndex = (i / 3) * 3 + j / 3;
                    int bit = 1 << num;
                
                    if ((rows[i] & bit) > 0 || 
                        (cols[j] & bit) > 0 || 
                        (boxes[boxIndex] & bit) > 0) {
                        return false;
                    }
                
                    rows[i] |= bit;
                    cols[j] |= bit;
                    boxes[boxIndex] |= bit;
                }
            }
        }
    
        return true;
    }
}

执行结果

哈希表版本:

  • 执行用时:88 ms
  • 内存消耗:43.2 MB

位运算版本:

  • 执行用时:80 ms
  • 内存消耗:42.8 MB

代码亮点

  1. 🎯 巧妙的3x3方格索引计算:boxIndex = (i / 3) * 3 + j / 3
  2. 💡 使用位运算优化空间和性能
  3. 🔍 一次遍历完成所有验证
  4. 🎨 代码结构清晰,易于理解和维护

常见错误分析

  1. 🚫 3x3宫格索引计算错误
  2. 🚫 没有跳过空白格子('.')
  3. 🚫 重复检查同一个位置
  4. 🚫 位运算实现时位移计算错误

解法对比

解法 时间复杂度 空间复杂度 优点 缺点
暴力验证 O(1) O(1) 直观简单 代码冗长
哈希表法 O(1) O(1) 易于理解 空间较大
位运算法 O(1) O(1) 性能最优 可读性差

相关题目

posted @   旧厂街小江  阅读(2)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
点击右上角即可分享
微信分享提示