36. 有效的数独

36. 有效的数独

题目描述

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

  1. 数字 1-9 在每一行只能出现一次。

  2. 数字 1-9 在每一列只能出现一次。

  3. 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。

在这里插入图片描述
上图是一个部分填充的有效的数独。

数独部分空格内已填入了数字,空白格用 '.' 表示。

示例1:

输入:
[
  ["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:

输入:
[
  ["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 存在, 因此这个数独是无效的。

说明:

  • 一个有效的数独(部分已被填充)不一定是可解的。
  • 只需要根据以上规则,验证已经填入的数字是否有效即可。
  • 给定数独序列只包含数字 1-9 和字符 '.'
  • 给定数独永远是 9x9 形式的。

题解:
法一:

使用三个二维数组,row[9][9], col[9][9], blo[9][9] ,分别记录 行、列、块 中每个元素的状态。

块的下标可以用 (i / 3) * 3 + j / 3 来表示。

二重循环遍历 board ,对于填充了数字的格子进行判断即可。

法一代码:

class Solution {
public:
    bool isValidSudoku(vector<vector<char>>& board) {
        vector<vector<bool>> row(9, vector<bool>(9, 0) );
        vector<vector<bool>> col(9, vector<bool>(9, 0) );
        vector<vector<bool>> blo(9, vector<bool>(9, 0) );
        int u, k;
        for ( int i = 8; i >= 0; --i ) {
            for ( int j = 8; j >= 0; --j ) {
                if ( board[i][j] == '.' ) continue;
                u = (board[i][j] & 15) - 1;
                if ( row[i][u] ) return false;
                if ( col[j][u] ) return false;
                k = (i / 3) * 3 + j / 3;
                if ( blo[k][u] ) return false;
                row[i][u] = col[j][u] = blo[k][u] = true;
            }
        }
        return true;
    }
};
/*
时间:16ms,击败:99.81%
内存:18.9MB,击败:37.29%
*/
法二:

其实法一中的数组第二维用来记录 1-9 是否出现过,而我们可以使用位运算来优化。

考虑使用一个整数 num ,初值为 0 ,假设当前数字为 9 ,那么 cnt |= 1 << 9 ,也就是把二进制中的相应位设置为 1 。这样可以优化掉 法一 中的第二维。

法二代码:

class Solution {
public:
    bool isValidSudoku(vector<vector<char>>& board) {
        vector<int> row(9, 0);
        vector<int> col(9, 0);
        vector<int> blo(9, 0);
        int u, k;
        for ( int i = 8; i >= 0; --i ) {
            for ( int j = 8; j >= 0; --j ) {
                if ( board[i][j] == '.' ) continue;
                u = board[i][j] & 15;
                if ( row[i] >> u & 1 ) return false;
                if ( col[j] >> u & 1 ) return false;
                k = (i / 3) * 3 + j / 3;
                if ( blo[k] >> u & 1 ) return false;
                row[i] |= (1 << u);
                col[j] |= (1 << u);
                blo[k] |= (1 << u);
            }
        }
        return true;
    }
};
/*
时间:20ms,击败:99.12%
内存:17.5MB,击败:99.03%
*/

法三:

法二中还可以进行优化,只使用一个数组,将数组中的前 9 位记录行的状态,中间 9 位记录列的状态,后面 9 位记录块的状态。int 能够表示的下。

法三代码:

class Solution {
public:
    bool isValidSudoku(vector<vector<char>>& board) {
        vector<int> rec(9, 0);
        int u, k;
        int r, c, b;
        for ( int i = 8; i >= 0; --i ) {
            for ( int j = 8; j >= 0; --j ) {
                if ( board[i][j] == '.' ) continue;
                u = board[i][j] & 15;
                r = 1 << u;
                if ( rec[i] & r ) return false;
                c = 1 << 9 << u;
                if ( rec[j] & c ) return false;
                b = 1 << 18 << u;
                k = (i / 3) * 3 + j / 3;
                if ( rec[k] & b ) return false;
                rec[i] |= r;
                rec[j] |= c;
                rec[k] |= b;
            }
        }
        return true;
    }
};
/*
时间:16ms,击败:99.81%
内存:17.6MB,击败:96.58%
*/

优化好像不大2333

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值