题目描述:
请你判断一个 9 x 9 的数独是否有效。只需要 根据以下规则 ,验证已经填入的数字是否有效即可。
数字 1-9 在每一行只能出现一次。
数字 1-9 在每一列只能出现一次。
数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。(请参考示例图)
注意:
一个有效的数独(部分已被填充)不一定是可解的。
只需要根据以上规则,验证已经填入的数字是否有效即可。
空白格用 '.' 表示。
题目链接
本人感觉此题目较为难理解,前天看了这道题给搁置了,然后今天做了387. 字符串中的第一个唯一字符这道题才有了较为清晰的思路。
首先创建三个 9* 9 的数组 cols, rows 和 tris
首先初始化三个数组,令它们每一个元素的值都为0
cols 存放的是每行各个元素中出现的次数。
比如实例1 中,board[0][0]='5', 由于board存放的数范围是1-9,那么在cols数组中令
cols[0][5-1]++ = cols[0][4]++,也就是在原来值的基础上加1. 由于board存的是字符类型,所以cols[0][5-1]的计算就是 cols[0][board[0][0]-'1'].
同样,board[1][1] = '6', 在cols数组中 cols[1][6-1]++ = cols[1][5]++ . 对于board中 '.' 的值略过。
同样,rows中,每行存的是 board 每列各元素出现的次数;
实例1 中,board[3][0]='8',在rows数组中 rows[0][8-1]++ = rows[0][7]++ = rows[0][board[3][0]-'1'];
board[2][1]='9',在rows数组中 rows[1][9-1]++ = rows[1][8]++;
下面重点来了,九宫格的计数~
由于一共有9个九宫格,从左到右,从上到下,依次标记每个九宫格索引值为0-2,3-5,6-8 如下图
那么在tris数组中,每行分别代表每个九宫格中元素出现的次数。
故已知某个元素的行和列,怎么添加到tris数组的对应位置是一个需要解决的问题。
对于tris的列,我们只需要根据元素来获取即可,所以关键是寻找tris的行,也就是根据一个元素的行和列判断它是在哪个九宫格当中。
每3个行位置和列位置更新一个九宫格,所以首先想到的肯定是除以3,如果让行 i/3 那对于第0个九宫格是可以的,而当i=3, 4, 5 时, i/3=1,那么第三个九宫格就不是3而是1. 所以需要再乘3. 也就是 i/3*3 能够准确的判断第0个,第3个和第6个九宫格。那么对于其他九宫格,我们就在列 j 上想办法。由于列也是每3个位置更新一个九宫格,故再让j/3,也就是 i/3*3+j/3 通过元素的行和列来验证此公式发现是可以的,这样我们就成功的解决了上面的问题。
将九宫格看成是 3*3 的数组,其中 i/3*3 代表的是九宫格行的位置。i/3 其实就是令每一个 i 处在第0、1、2三个位置,而九宫格不只有三行,还有三列,所以再乘3 令每个九宫格的行位置准确。而九宫格的列就是 j/3,行+列就代表九宫格的位置。
下面是代码展示
bool isValidSudoku(char** board, int boardSize, int* boardColSize){
int rows[9][9];
int cols[9][9];
int tris[9][9];
// 数组初始化 令每个元素都为0
memset(rows, 0, sizeof(rows));
memset(cols, 0, sizeof(cols));
memset(tris, 0, sizeof(tris));
// 将行、列和九宫格元素存在的个数分别存到三个数组中
for(int i=0; i<9; ++i)
{
for(int j=0; j<9; ++j)
{
if(board[i][j]!='.')
{
int temp = board[i][j];
rows[i][temp-'1']++;
tris[i/3*3+j/3][temp-'1']++;
}
if(board[j][i]!='.')
{
int temp = board[j][i];
cols[i][temp-'1']++;
}
}
}
// 判断 三个数组中是否有大于1的元素
for(int i=0; i<9; ++i)
{
for(int j=0; j<9; ++j)
{
if(rows[i][j]>1 || cols[i][j]>1 || tris[i][j]>1)
{
return false;
}
}
}
return true;
}