LeetCode 面试经典 150_矩阵_有效的数独(34_36_C++_中等)
题目描述:
请你判断一个 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
解释:除了第一行的第一个数字从 5 改为 8 以外,空格内其他数字均与 示例1 相同。 但由于位于左上角的 3x3 宫内有两个 8 存在, 因此这个数独是无效的。
提示:
board.length== 9
board[i].length == 9
board[i][j] 是一位数字(1-9)或者 ‘.’9
题解:
解题思路:
思路一(暴力破解):
1、具体思路如下
- 判断每一行,数字 1-9 在每一行只能出现一次。
- 判断每一列,数字 1-9 在每一列只能出现一次。
- 判断每一 3x3 宫内,数字 1-9 只能出现一次。
使用哈希集合来判断每个数字出现的次数,出现过的数字会存储在哈希集合中,若再次出现则返回 false。
2、复杂度分析:
① 时间复杂度:O(1)。数独共有 81 个单元格,只需要对每个单元格遍历一次即可。
② 空间复杂度:O(1)。由于数独的大小固定,因此哈希表的空间也是固定的。
思路二(额外数组:遍历一遍数组):
1、使用数组代替哈希表,使用三个数组来存储三类的判断。
- rows[9][9] rows[i][index] 代表第 i 行,数字 index+1 出现的次数。
- cols[9][9] cols[j][index] 代表第 j 列,数字 index+1 出现的次数。
- subboxs[3][3][9] subboxs[i/3][j/3][index] 代表第 (i/3, j/3) 子宫格中数字 index+1 出现的次数。
2、复杂度分析
① 时间复杂度:O(1)。数独共有 81 个单元格,只需要对每个单元格遍历一次即可。
② 空间复杂度:O(1)。由于数独的大小固定,因此哈希表的空间也是固定的。
代码实现
代码实现(思路一(暴力破解)):
class Solution1 {
public:
bool isValidSudoku(vector<vector<char>>& board) {
int rowSize = board.size(); // 获取数独棋盘的行数
int colSize = board[0].size(); // 获取数独棋盘的列数
unordered_set<char> set; // 用于存储当前行、列和 3x3 宫的已见数字,确保数字不重复
// 判断每一行,数字 1-9 在每一行只能出现一次
for (int i = 0; i < rowSize; i++) { // 遍历每一行
set.clear(); // 清空 set,用于存储当前行已经出现的数字
for (int j = 0; j < colSize; j++) { // 遍历当前行的每一列
if (board[i][j] != '.') { // 如果该位置是一个数字(不是 '.')
if (set.count(board[i][j])) { // 如果数字已经出现过
return false; // 数字重复,返回 false
}
set.insert(board[i][j]); // 否则,加入 set,标记这个数字已经出现过
}
}
}
// 判断每一列,数字 1-9 在每一列只能出现一次
for (int j = 0; j < colSize; j++) { // 遍历每一列
set.clear(); // 清空 set,用于存储当前列已经出现的数字
for (int i = 0; i < rowSize; i++) { // 遍历当前列的每一行
if (board[i][j] != '.') { // 如果该位置是一个数字(不是 '.')
if (set.count(board[i][j])) { // 如果数字已经出现过
return false; // 数字重复,返回 false
}
set.insert(board[i][j]); // 否则,加入 set,标记这个数字已经出现过
}
}
}
// 判断每一个 3x3 宫内,数字 1-9 只能出现一次
for (int i = 0; i < rowSize; i += 3) { // 遍历每一个 3x3 宫的起始行
for (int j = 0; j < colSize; j += 3) { // 遍历每一个 3x3 宫的起始列
set.clear(); // 清空 set,用于存储当前 3x3 宫内已经出现的数字
for (int row = 0; row < 3; row++) { // 遍历 3x3 宫内的行
for (int col = 0; col < 3; col++) { // 遍历 3x3 宮内的列
char c = board[i + row][j + col]; // 获取当前 3x3 宮内的数字
if (c != '.') { // 如果当前位置有数字
if (set.count(c)) { // 如果该数字已经出现过
return false; // 数字重复,返回 false
}
set.insert(c); // 否则,加入 set,标记这个数字已经出现过
}
}
}
}
}
return true; // 如果没有重复数字,返回 true,表示数独有效
}
};
代码实现(思路二(额外数组:遍历一遍数组)):
class Solution2 {
public:
bool isValidSudoku(vector<vector<char>>& board) {
// 创建三个数组来记录每一行、每一列和每个子宫格中数字出现的次数
int rows[9][9]; // rows[i][index] 代表第 i 行,数字 index+1 出现的次数
int cols[9][9]; // cols[j][index] 代表第 j 列,数字 index+1 出现的次数
int subboxs[3][3][9]; // subboxs[i/3][j/3][index] 代表第 (i/3, j/3) 子宫格中数字 index+1 出现的次数
// 初始化三个数组,将所有计数值设为 0
memset(rows,0,sizeof(rows)); // 将所有行的数字计数清零
memset(cols,0,sizeof(cols)); // 将所有列的数字计数清零
memset(subboxs,0,sizeof(subboxs)); // 将所有子宫格的数字计数清零
// 遍历数独棋盘的每个格子
for (int i = 0; i < 9; i++) {
for (int j = 0; j < 9; j++) {
char c = board[i][j]; // 获取当前格子的值
if (c != '.') { // 如果当前格子是一个数字(即不为空)
int index = c - '1'; // 将字符转换为数字,'1' 对应 index 0,'9' 对应 index 8
// 更新行、列和子宫格中该数字的计数
rows[i][index]++; // 更新第 i 行中该数字出现的次数
cols[j][index]++; // 更新第 j 列中该数字出现的次数
subboxs[i / 3][j / 3][index]++; // 更新第 (i/3, j/3) 子宫格中该数字出现的次数
// 如果该数字在行、列或子宫格中出现超过一次,返回 false
if (rows[i][index] > 1 || cols[j][index] > 1 || subboxs[i / 3][j / 3][index] > 1) {
return false; // 数字重复,数独无效
}
}
}
}
// 如果没有发现重复数字,返回 true,表示数独有效
return true;
}
};
以思路二为例进行调试
#include<iostream>
#include<vector>
#include<unordered_set>
#include<cstring>
using namespace std;
class Solution2 {
public:
bool isValidSudoku(vector<vector<char>>& board) {
// 创建三个数组来记录每一行、每一列和每个子宫格中数字出现的次数
int rows[9][9]; // rows[i][index] 代表第 i 行,数字 index+1 出现的次数
int cols[9][9]; // cols[j][index] 代表第 j 列,数字 index+1 出现的次数
int subboxs[3][3][9]; // subboxs[i/3][j/3][index] 代表第 (i/3, j/3) 子宫格中数字 index+1 出现的次数
// 初始化三个数组,将所有计数值设为 0
memset(rows,0,sizeof(rows)); // 将所有行的数字计数清零
memset(cols,0,sizeof(cols)); // 将所有列的数字计数清零
memset(subboxs,0,sizeof(subboxs)); // 将所有子宫格的数字计数清零
// 遍历数独棋盘的每个格子
for (int i = 0; i < 9; i++) {
for (int j = 0; j < 9; j++) {
char c = board[i][j]; // 获取当前格子的值
if (c != '.') { // 如果当前格子是一个数字(即不为空)
int index = c - '1'; // 将字符转换为数字,'1' 对应 index 0,'9' 对应 index 8
// 更新行、列和子宫格中该数字的计数
rows[i][index]++; // 更新第 i 行中该数字出现的次数
cols[j][index]++; // 更新第 j 列中该数字出现的次数
subboxs[i / 3][j / 3][index]++; // 更新第 (i/3, j/3) 子宫格中该数字出现的次数
// 如果该数字在行、列或子宫格中出现超过一次,返回 false
if (rows[i][index] > 1 || cols[j][index] > 1 || subboxs[i / 3][j / 3][index] > 1) {
return false; // 数字重复,数独无效
}
}
}
}
// 如果没有发现重复数字,返回 true,表示数独有效
return true;
}
};
int main(int argc, char const *argv[])
{
vector<vector<char>> 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'}}};
Solution2 s;
if (s.isValidSudoku(board)){
cout<<"true"<<endl;
}else{
cout<<"false"<<endl;
}
return 0;
}
LeetCode 面试经典 150_矩阵_有效的数独(34_36)原题链接
欢迎大家和我沟通交流(✿◠‿◠)