LeetCode 面试经典 150_矩阵_有效的数独(34_36_C++_中等)(额外数组)
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 出现的次数// 初始化三个数组,将所有计数值设为 0memset(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) 子宫格中该数字出现的次数// 如果该数字在行、列或子宫格中出现超过一次,返回 falseif (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 出现的次数// 初始化三个数组,将所有计数值设为 0memset(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) 子宫格中该数字出现的次数// 如果该数字在行、列或子宫格中出现超过一次,返回 falseif (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)原题链接
欢迎大家和我沟通交流(✿◠‿◠)