LeetCode 面试经典 150_矩阵_有效的数独(34_36_C++_中等)(额外数组)

LeetCode 面试经典 150_矩阵_有效的数独(34_36_C++_中等)

题目描述:

请你判断一个 9 x 9 的数独是否有效。只需要 根据以下规则 ,验证已经填入的数字是否有效即可。

  1. 数字 1-9 在每一行只能出现一次。
  2. 数字 1-9 在每一列只能出现一次。
  3. 数字 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)。由于数独的大小固定,因此哈希表的空间也是固定的。

代码实现

代码实现(思路一(暴力破解)):
cpp 复制代码
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,表示数独有效
    }
};
代码实现(思路二(额外数组:遍历一遍数组)):
cpp 复制代码
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;  
    }
};
以思路二为例进行调试
cpp 复制代码
#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)原题链接

欢迎大家和我沟通交流(✿◠‿◠)

相关推荐
Java中文社群6 小时前
面试官:如何确保动态线程池任务都执行完?
java·后端·面试
前端fighter7 小时前
深入解析CSS定位:Sticky与Fixed的异同与实战应用
前端·css·面试
liang_jy7 小时前
Java volatile
android·java·面试
南北是北北7 小时前
Flow 热流
前端·面试
企鹅chi月饼8 小时前
面试问题:c++的内存管理方式,delete的使用,vector的resize和reverse,容量拓展
c++·面试
南北是北北8 小时前
为什么会出现有声无画/黑屏,以及如何避免与优化
前端·面试
南北是北北8 小时前
VSync 是什么、ExoPlayer 怎么对齐 VSync 与音画同步、常见问题与调参要点
前端·面试
前端fighter8 小时前
前端开发中的模块化:从 CommonJS 到 ES6 模块
前端·javascript·面试
你说今年的枫叶好像不够红啊8 小时前
LeetCode[两数之和] java版
数据结构·算法·leetcode