36. 有效的数独
题目描述
判断一个 9x9 的数独是否有效。只需要根据以下规则,验证已经填入的数字是否有效即可。
-
数字
1-9
在每一行只能出现一次。 -
数字
1-9
在每一列只能出现一次。 -
数字
1-9
在每一个以粗实线分隔的3x3
宫内只能出现一次。
上图是一个部分填充的有效的数独。
数独部分空格内已填入了数字,空白格用 '.'
表示。
示例1:
输入:
[
["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:
输入:
[
["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
解释: 除了第一行的第一个数字从 5 改为 8 以外,空格内其他数字均与 示例1 相同。
但由于位于左上角的 3x3 宫内有两个 8 存在, 因此这个数独是无效的。
说明:
- 一个有效的数独(部分已被填充)不一定是可解的。
- 只需要根据以上规则,验证已经填入的数字是否有效即可。
- 给定数独序列只包含数字
1-9
和字符'.'
。 - 给定数独永远是
9x9
形式的。
题解:
法一:
使用三个二维数组,row[9][9], col[9][9], blo[9][9]
,分别记录 行、列、块
中每个元素的状态。
块的下标可以用 (i / 3) * 3 + j / 3
来表示。
二重循环遍历 board
,对于填充了数字的格子进行判断即可。
法一代码:
class Solution {
public:
bool isValidSudoku(vector<vector<char>>& board) {
vector<vector<bool>> row(9, vector<bool>(9, 0) );
vector<vector<bool>> col(9, vector<bool>(9, 0) );
vector<vector<bool>> blo(9, vector<bool>(9, 0) );
int u, k;
for ( int i = 8; i >= 0; --i ) {
for ( int j = 8; j >= 0; --j ) {
if ( board[i][j] == '.' ) continue;
u = (board[i][j] & 15) - 1;
if ( row[i][u] ) return false;
if ( col[j][u] ) return false;
k = (i / 3) * 3 + j / 3;
if ( blo[k][u] ) return false;
row[i][u] = col[j][u] = blo[k][u] = true;
}
}
return true;
}
};
/*
时间:16ms,击败:99.81%
内存:18.9MB,击败:37.29%
*/
法二:
其实法一中的数组第二维用来记录 1-9
是否出现过,而我们可以使用位运算来优化。
考虑使用一个整数 num
,初值为 0
,假设当前数字为 9
,那么 cnt |= 1 << 9
,也就是把二进制中的相应位设置为 1
。这样可以优化掉 法一 中的第二维。
法二代码:
class Solution {
public:
bool isValidSudoku(vector<vector<char>>& board) {
vector<int> row(9, 0);
vector<int> col(9, 0);
vector<int> blo(9, 0);
int u, k;
for ( int i = 8; i >= 0; --i ) {
for ( int j = 8; j >= 0; --j ) {
if ( board[i][j] == '.' ) continue;
u = board[i][j] & 15;
if ( row[i] >> u & 1 ) return false;
if ( col[j] >> u & 1 ) return false;
k = (i / 3) * 3 + j / 3;
if ( blo[k] >> u & 1 ) return false;
row[i] |= (1 << u);
col[j] |= (1 << u);
blo[k] |= (1 << u);
}
}
return true;
}
};
/*
时间:20ms,击败:99.12%
内存:17.5MB,击败:99.03%
*/
法三:
法二中还可以进行优化,只使用一个数组,将数组中的前 9
位记录行的状态,中间 9
位记录列的状态,后面 9
位记录块的状态。int
能够表示的下。
法三代码:
class Solution {
public:
bool isValidSudoku(vector<vector<char>>& board) {
vector<int> rec(9, 0);
int u, k;
int r, c, b;
for ( int i = 8; i >= 0; --i ) {
for ( int j = 8; j >= 0; --j ) {
if ( board[i][j] == '.' ) continue;
u = board[i][j] & 15;
r = 1 << u;
if ( rec[i] & r ) return false;
c = 1 << 9 << u;
if ( rec[j] & c ) return false;
b = 1 << 18 << u;
k = (i / 3) * 3 + j / 3;
if ( rec[k] & b ) return false;
rec[i] |= r;
rec[j] |= c;
rec[k] |= b;
}
}
return true;
}
};
/*
时间:16ms,击败:99.81%
内存:17.6MB,击败:96.58%
*/
优化好像不大2333