一个9x9的数字矩阵,每个格子可以是1-9的整数,每行每列的数值不重复,将9x9的矩阵分成3x3个九宫格,每个九宫格的数值也不重复。就认为是一个有效的数独。
只要符合上述规则即为有效,不用考虑数独是否可解。
输出的字符串,空格用点号代替。
刷题请直接到有效的数独
输入
[“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
本来我打算先把字符数组转存为int数组,然后再对int数组操作。后来发现没有必要。需要用的话再转换就可以。
暴力求解。
每行、每列、每个九宫格分别判断。
判断时,创建一个bool数组,遍历当下需要判断的9个数,如果出现过就标记为true,如果读到的数被标记过,那么该数出现过,说明该数独无效。
时间复杂度O(n^3)
#include
#include
char board[10][10];
struct myList{
int value[9];
myList(){
memset(value, 0, sizeof(value));
}
};
bool check(myList l){
bool isAppeared[10];
memset(isAppeared, 0, sizeof(isAppeared));
for(int i=0; i<9; i++){
while(i<9 && l.value[i] == 0) i++;
if(i == 9) break;
// printf("%d \n",isAppeared[l.value[i]]);
if(isAppeared[l.value[i]] == true)
return false;
isAppeared[l.value[i]] = true;
}
return true;
}
int char2int(char c){
if(c == '.') return 0;
return (int)c-(int)'0';
}
bool isValidSudoku(){
int n = 9;
//chack by row
for(int i=0; i<n; i++){
myList l;
for(int j=0; j<n; j++)
l.value[j] = char2int(board[i][j]);
if(!check(l)) return false;
}
//chack by col
for(int i=0; i<n; i++){
myList l;
for(int j=0; j<n; j++)
l.value[j] = char2int(board[j][i]);
if(!check(l)) return false;
}
//chack by 3*3
for(int i=0; i<3; i++){
for(int j=0; j<3; j++){
myList l;
int tmp = 0;
for(int a=0; a<3; a++)
for(int b=0; b<3; b++)
l.value[tmp++] = char2int(board[i*3+a][j*3+b]);
if(!check(l)) return false;
}
}
return true;
}
int main(){
//53..7....
//6..195...
//.98....6.
//8...6...3
//4..8.3..1
//7...2...6
//.6....28.
//...419..5
//....8..79
for(int a=0; a<9; a++)
scanf("%s", board[a]);
// for(int a=0; a<9; a++)
// printf("%s\n", board[a]);
printf("%d", isValidSudoku());
return 0;
}
其实跟本地代码没什么差别
struct myList{
int value[9];
myList(){
memset(value, 0, sizeof(value));
}
};
bool check(myList l){
bool isAppeared[10];
memset(isAppeared, 0, sizeof(isAppeared));
for(int i=0; i<9; i++){
while(i<9 && l.value[i] == 0) i++;
if(i == 9) break;
// printf("%d \n",isAppeared[l.value[i]]);
if(isAppeared[l.value[i]] == true)
return false;
isAppeared[l.value[i]] = true;
}
return true;
}
int char2int(char c){
if(c == '.') return 0;
return (int)c-(int)'0';
}
class Solution {
public:
bool isValidSudoku(vector<vector<char>>& board) {
int n = 9;
//chack by row
for(int i=0; i<n; i++){
myList l;
for(int j=0; j<n; j++)
l.value[j] = char2int(board[i][j]);
if(!check(l)) return false;
}
//chack by col
for(int i=0; i<n; i++){
myList l;
for(int j=0; j<n; j++)
l.value[j] = char2int(board[j][i]);
if(!check(l)) return false;
}
//chack by 3*3
for(int i=0; i<3; i++){
for(int j=0; j<3; j++){
myList l;
int tmp = 0;
for(int a=0; a<3; a++)
for(int b=0; b<3; b++)
l.value[tmp++] = char2int(board[i*3+a][j*3+b]);
if(!check(l)) return false;
}
}
return true;
}
};
提交报错Error: reference to 'list' is ambiguous
原因是我声明了一个结构体叫list,而c++本来有一个list类。
参考Error: reference to ‘list’ is ambiguous
提交时还遇到总是报错isAppeared数组下标总是超界
原因是读取l.value[i]对应的isAppeared值,一开始结构体myList没写构造函数,也就是没有初始化,里边的值可能是任意值,所以会出现isAppeared下标超界。
空间换时间。应该可以在O(n^2)内解决。
声明9(行)+9(列)+9(九宫格)个大小为9的标记数组,遍历原始数组,直接在标记数组中操作,如果出现冲突(被标记过)就直接返回false。
#include
#include
char board[10][10];
bool rAppeared[9][10], cAppeared[9][10], nAppeared[9][10];
bool isValidSudoku(){
int n = 9;
for(int i=0; i<n; i++){
for(int j=0; j<n; j++){
if(board[i][j]!='.'){
int t = board[i][j] - '0';
if(rAppeared[i][t] !=0 ) return false;
else rAppeared[i][t] = 1;
if(cAppeared[j][t] !=0 ) return false;
else cAppeared[j][t] = 1;
int n = (i/3)*3+(j/3);
if(nAppeared[n][t] != 0) return false;
else nAppeared[n][t] = 1;
}
}
}
return true;
}
int main(){
for(int a=0; a<9; a++)
scanf("%s", board[a]);
printf("%d", isValidSudoku());
return 0;
}
bool rAppeared[9][10], cAppeared[9][10], nAppeared[9][10];
class Solution {
public:
bool isValidSudoku(vector<vector<char>>& board) {
memset(rAppeared, 0 , sizeof(rAppeared));
memset(cAppeared, 0 , sizeof(cAppeared));
memset(nAppeared, 0 , sizeof(nAppeared));
int n = 9;
for(int i=0; i<n; i++){
for(int j=0; j<n; j++){
if(board[i][j]!='.'){
int t = board[i][j] - '0';
if(rAppeared[i][t] !=0 ) return false;
else rAppeared[i][t] = 1;
if(cAppeared[j][t] !=0 ) return false;
else cAppeared[j][t] = 1;
int n = (i/3)*3+(j/3);
if(nAppeared[n][t] != 0) return false;
else nAppeared[n][t] = 1;
}
}
}
return true;
}
};
提交过程中出现同一组测试数据,期望结果是true,本地结果是true,但提交结果是false的情况。原因是本地代码中三个标记数组rAppeared、cAppeared和nAppeared是全局变量,每个元素都会被自动初始化为0。但是提交代码中没有自动初始化,也没有手动初始化,导致结果错误。
看了别人的题解,发现大家的做法和我的解决思路2一致,都是一次遍历整个数独矩阵,确认当前元素是否在当前行、当前列、当前九宫格出现过,如果出现过直接返回false,如果没有出现过则标记为已经出现过。
但是,别人的代码比我写的优雅。复制一个Java版的,供自己学习。
class Solution {
public boolean isValidSudoku(char[][] board) {
// 记录某行,某位数字是否已经被摆放
boolean[][] row = new boolean[9][9];
// 记录某列,某位数字是否已经被摆放
boolean[][] col = new boolean[9][9];
// 记录某 3x3 宫格内,某位数字是否已经被摆放
boolean[][] block = new boolean[9][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 blockIndex = i / 3 * 3 + j / 3;
if (row[i][num] || col[j][num] || block[blockIndex][num]) {
return false;
} else {
row[i][num] = true;
col[j][num] = true;
block[blockIndex][num] = true;
}
}
}
}
return true;
}
}
参考官方题解