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

发布于:2025-09-04 ⋅ 阅读:(18) ⋅ 点赞:(0)

题目描述:

请你判断一个 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)。由于数独的大小固定,因此哈希表的空间也是固定的。

代码实现

代码实现(思路一(暴力破解)):
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)原题链接
欢迎大家和我沟通交流(✿◠‿◠)


网站公告

今日签到

点亮在社区的每一天
去签到