LeetCode 第36题:有效的数独
LeetCode 第36题:有效的数独
题目描述
请你判断一个 9 x 9 的数独是否有效。只需要 根据以下规则 ,验证已经填入的数字是否有效即可。
- 数字 1-9 在每一行只能出现一次。
- 数字 1-9 在每一列只能出现一次。
- 数字 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"]]
输出: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 == 9board[i].length == 9board[i][j]是一个数字1-9或者'.'
解题思路
方法一:哈希表记录
使用三个不同的哈希表分别记录每行、每列、每个3x3方格中数字的出现情况。
关键点:
- 使用哈希集合记录每个区域的数字
- 巧妙地计算3x3方格的索引
- 一次遍历完成所有验证
具体步骤:
- 创建三个哈希表数组:
rows[9]: 记录每行的数字cols[9]: 记录每列的数字boxes[9]: 记录每个3x3方格的数字
- 遍历整个数独板:
- 计算当前位置属于哪个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
代码亮点
- 🎯 巧妙的3x3方格索引计算:
boxIndex = (i / 3) * 3 + j / 3 - 💡 使用位运算优化空间和性能
- 🔍 一次遍历完成所有验证
- 🎨 代码结构清晰,易于理解和维护
常见错误分析
- 🚫 3x3宫格索引计算错误
- 🚫 没有跳过空白格子('.')
- 🚫 重复检查同一个位置
- 🚫 位运算实现时位移计算错误
解法对比
| 解法 | 时间复杂度 | 空间复杂度 | 优点 | 缺点 |
|---|---|---|---|---|
| 暴力验证 | O(1) | O(1) | 直观简单 | 代码冗长 |
| 哈希表法 | O(1) | O(1) | 易于理解 | 空间较大 |
| 位运算法 | O(1) | O(1) | 性能最优 | 可读性差 |
相关题目
- LeetCode 37. 解数独 - 困难
- LeetCode 73. 矩阵置零 - 中等
- LeetCode 289. 生命游戏 - 中等
- LeetCode 419. 甲板上的战舰 - 中等

浙公网安备 33010602011771号