【LeetCode 36】位运算的神奇解题思路

以下文章来源于李哥技术笔记 ,作者我是李同学

如有侵权,联系删除

题目描述

请你判断一个 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)或者 '.'

题解

这道题不是让你去解一个数独,而是要去判断这个数独合不合法,也就是看每一行,每一列以及每一个小的九宫格中是否只出现一次1~9。

方案一:

我们只需要设置三个数组分别统计每一行、每一列以及每一个九宫格出现数字的次数,只要发现重复,就说明该数独不是有效的。

NxN的矩阵通过两次嵌套for循环可以遍历完成,行和列比较好统计。​​​​​​​

for(int i=0; i<N; i++){    for(int j=0; j<N; j++){          ......    }}

但统计九宫格内的元素需要和循环的i,j挂上钩。

图片

我们先把整个矩阵划分为9个9宫格,先看左上角的9宫格(i=0, j=0~9)

图片

​​​​​​​

(0,0)  (0,1)  (0,2)(1,0)  (1,1)  (1,2)(2,0)  (2,1)  (2,2)

假设其中某个元素是第a行b列,那么可以推出a,b和i,j的关系。​​​​​​​

a=j/3b=j%3

当然,这是在起点是(0,0)的情况下,9宫格内的元素公式。

其他起点和i,j的关系(此时i=0~9, j=0)

图片

​​​​​​​

A=(i/3)*3B=(i%3)*3

最后我们整理两个公式可得通项公式​​​​​​​

a=(i/3)*3+j/3b=(i%3)*3+j%3

代码​​​​​​

class Solution {    public boolean isValidSudoku(char[][] board) {        for(int i=0; i<9; i++){            Set setLine = new HashSet();            Set setCol = new HashSet();            Set setBox = new HashSet();            for(int j = 0; j<9; j++){                if(board[i][j]!='.' && !setLine.add(board[i][j])){                    return false;                }                if(board[j][i]!='.' && !setCol.add(board[j][i])){                    return false;                }                int a = (i/3)*3 + j/3;                int b = (i%3)*3 + j%3;                if(board[a][b] != '.' && !setBox.add(board[a][b])){                    return false;                }            }        }        return true;    }}

方案二:

int类型是32位,用位数来表示1~9,这种方法使用一维数组,直接用位运算存储和判断。

具体如图:

图片

代码​​​​​​​

class Solution {        public boolean isValidSudoku(char[][] board) {        int[] line = new int[9];        int[] column = new int[9];        int[] cell = new int[9];        int shift = 0;        for (int i = 0; i < 9; i++) {            for (int j = 0; j < 9; j++) {                //如果还没有填数字,直接跳过                if (board[i][j] == '.')                    continue;                shift = 1 << (board[i][j] - '1');                int k = (i / 3) * 3 + j / 3;                //如果对应的位置只要有一个大于0,说明有冲突,直接返回false                if ((column[i] & shift) > 0 || (line[j] & shift) > 0 || (cell[k] & shift) > 0)                    return false;                column[i] |= shift;                line[j] |= shift;                cell[k] |= shift;            }        }        return true;    }}

图片

  • 5
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值