LeetCode 36. 有效的数独
题目描述



题意分析
棋盘固定为
9 × 9,每个已填数字在所在行、所在列和所在3 × 3宫中都不能重复。字符'.'代表空格,不参与检查。题目只验证当前已填数字是否冲突。即使这些数字无法补成完整数独,只要暂时不违反三类规则,也应返回
true,因此不需要搜索空格的填法。
解法:行、列、宫三组标记
核心思路
[!blue]
检查当前数字时,只需要知道它此前是否在同一行、列或宫中出现过,不需要保存出现次数或位置。数字只有
1到9,用布尔数组即可分别记录三类信息:rows[row][num]表示第row行出现过数字num + 1,cols和boxes同理。每个宫占连续三行、三列,所以
row / 3和col / 3分别是宫的行、列编号,取值均为0到2。把宫的二维坐标按行展开,就得到唯一编号(row / 3) * 3 + col / 3,范围为0到8。扫描每个非空格子前,三组标记只包含已经处理的格子。若当前数字的任意对应标记为真,就存在另一个已填格与它违反同一规则,可以立即返回
false。否则将三处标记同时设为真,处理完当前格后,这个不变量仍然成立。如果棋盘存在重复数字,它们中后扫描到的那个一定会读到先前留下的标记;如果扫描结束仍未发现冲突,就说明每行、每列、每宫都没有重复。这样一次遍历就完整检查了三类约束。
解题步骤
- 创建
rows[9][9]、cols[9][9]、boxes[9][9]三组布尔标记。- 逐格扫描棋盘,遇到
'.'直接继续。- 将字符转换为数字下标
num = board[row][col] - '1'。- 计算宫编号
box = (row / 3) * 3 + col / 3。- 若三组对应标记任一为真,立即返回
false;否则把三处都置为真。- 全部扫描完没有冲突则返回
true。减去
'1'是为了把数字1到9映射到数组下标0到8。必须先跳过空格,再做转换。全空棋盘没有任何已填数字冲突,会自然返回true;题目已经保证棋盘尺寸和字符范围合法,无需另行校验格式。
代码实现
class Solution {
public boolean isValidSudoku(char[][] board) {
boolean[][] rows = new boolean[9][9];
boolean[][] cols = new boolean[9][9];
boolean[][] boxes = new boolean[9][9];
for (int row = 0; row < 9; row++) {
for (int col = 0; col < 9; col++) {
if (board[row][col] == '.') {
continue;
}
int num = board[row][col] - '1';
// 宫坐标先按三分组,再以每行三个宫展开为一维编号。
int box = (row / 3) * 3 + col / 3;
// 先查三个约束,再写标记,不能让当前数字与自身冲突。
if (rows[row][num] || cols[col][num] || boxes[box][num]) {
return false;
}
rows[row][num] = true;
cols[col][num] = true;
boxes[box][num] = true;
}
}
return true;
}
}
func isValidSudoku(board [][]byte) bool {
var rows, cols, boxes [9][9]bool
for row := 0; row < 9; row++ {
for col := 0; col < 9; col++ {
if board[row][col] == '.' {
continue
}
num := board[row][col] - '1'
// 宫坐标先按三分组,再以每行三个宫展开为一维编号。
box := row/3*3 + col/3
// 先查三个约束,再写标记,不能让当前数字与自身冲突。
if rows[row][num] || cols[col][num] || boxes[box][num] {
return false
}
rows[row][num] = true
cols[col][num] = true
boxes[box][num] = true
}
}
return true
}
复杂度分析
- 时间复杂度:$O(1)$,固定扫描 81 个格子。
- 空间复杂度:$O(1)$,三组标记表大小固定。
关键点总结
[!green]
- 一个格子同时属于一行、一列、一个宫,因此三类约束可在一次遍历中完成。
- 宫编号公式来自二维宫坐标到一维编号的映射。
- 必须先检查再写标记,否则每个数字都会与自己冲突。
- 题目只要求验证已填数字是否合法,不要求判断棋盘是否可解。
易错点总结
[!yellow]
- 宫编号漏乘 3,例如写成
row / 3 + col / 3,会把不同宫错误合并。- 字符减
'0'会让'9'映射到下标 9,正确写法是减'1'。- 忘记跳过
'.',空格字符减'1'后不是合法数字下标,会导致数组越界。- 三类冲突条件应使用逻辑或;任意一类重复都应立即失败。
- 本题验证的是当前状态,不需要回溯补全空格,那是 37《解数独》的任务。
相似题目
| 题目 | 难度 | 关联与区别 |
|---|---|---|
| 37. 解数独 | 困难 | 本题只检查已有数字不冲突,原题还要填空并在回溯中维护同样的行列宫约束。 |
| 51. N 皇后 | 困难 | 同样用独立约束表判断位置能否使用,皇后题的约束是列及对角线。 |
转载与许可
许可
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!