LeetCode 第36题:有效的数独
1.LeetCode 第10题:正则表达式匹配2.LeetCode 第1题:两数之和3.LeetCode 第2题:两数相加4.LeetCode 第3题:无重复字符的最长子串5.LeetCode 第4题:寻找两个正序数组的中位数6.LeetCode 第8题:字符串转换整数 (atoi)7.LeetCode 第7题:整数反转8.LeetCode 第6题:Z字形变换9.LeetCode 第5题:最长回文子串10.LeetCode 第9题:回文数11.LeetCode 第11题:盛最多水的容器12.LeetCode 第12题:整数转罗马数字13.LeetCode 第13题:罗马数字转整数14.LeetCode 第14题:最长公共前缀15.LeetCode 第15题:三数之和16.LeetCode 第16题:最接近的三数之和17.LeetCode 第17题:电话号码的字母组合18.LeetCode 第18题:四数之和19.LeetCode 第19题:删除链表的倒数第N个结点20.LeetCode 第20题:有效的括号21.LeetCode 第21题:合并两个有序链表22.LeetCode 第22题:括号生成23.LeetCode 第23题:合并K个升序链表24.LeetCode 第24题:两两交换链表中的节点25.LeetCode 第25题:K 个一组翻转链表26.LeetCode 第26题:删除有序数组中的重复项27.LeetCode 第27题:移除元素28.LeetCode 第28题:找出字符串中第一个匹配项的下标29.LeetCode 第29题:两数相除30.LeetCode 第30题:串联所有单词的子串31.LeetCode 第31题:下一个排列32.LeetCode 第32题:最长有效括号33.LeetCode 第33题:搜索旋转排序数组34.LeetCode 第34题:在排序数组中查找元素的第一个和最后一个位置35.LeetCode 第35题:搜索插入位置
36.LeetCode 第36题:有效的数独
37.LeetCode 第37题:解数独38.LeetCode 第38题:外观数列39.LeetCode 第39题:组合总和40.LeetCode 第40题:组合总和 II41.LeetCode 第41题:缺失的第一个正数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 == 9
board[i].length == 9
board[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. 甲板上的战舰 - 中等
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了