算法-数独判断

473 阅读3分钟

前言

今天同事推了一道很有意思的算法题给我看,正好我本人平时也很喜欢写数独,看到这道题就有点小兴趣了,而且有一些比较好玩的思路,在此分享一下。

题目

其实简单说一下,这里就是给你输入一个 「 数独 」 写到一半「 也有可能写完了 」的二维数组,没填下的空用 “.” 代替,我们只需要把这个数组目前写的所有数做判断返回用户填的是对还是错的就好了!

示例 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

例1

示例 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

例2

PS:只需要判断当前,当前,当前是对的还是错的就好了。

思路

这里的话简单的解法我就不详细说明了,普遍都是用map存,然后判断这样。

但这里我一开始是想到如何用位运算来解题的,不过思路不太正确,最后再结合一个大佬的思路,自己撸了一遍,终于ac,感觉十分好玩。

初步想法

假如说,把数组的高宽和每个小方块分开的话,是需要有27种判断的情况的(数独的基本规则了)。

  • 横着1-9不能重复。
  • 竖着1-9不能重复。
  • 3*3个方块,1-9不能重复。

那对于 1-9 这9个数字来说,意味着什么呢?

这里拿数字8来讲。我们初始化一个 「 二进制的数 」 ,作为8在 「 数组里面相应的位置 」

这个二进制数一共有27位:

  • 前9位代表8在横向的位置。
  • 中间9位代表8在竖向的位置。
  • 最后9位代表8在方块里的位置。

这里分成3行给大家看,方便理解。

具体

那我们来看例1吧: 例1对应1的位置:[1, 3][4, 8]

找到第一个1的时候:

[1,3]的位置是 「 第2行 」 「 第4列 」 「 第2个方块 」

对应的二进制位置就应该是:

第一个1的位置

找到第二个1的时候:

[4,8]的位置是 「 第5行 」 「 第9列 」 「 第6个方块 」

对应的二进制位置就应该是:

第二个1的位置

看到这里你是否已经有一点思路了?

  • 假如说有两个重复的位置,我们只需要进行 「 与运算 」 ,既可以看的出来
  • 假如没有重复的,我们把他们进行 「 或运算 」 ,保留新结果。

这里设想一下,假如全部填完,并且正确的话,每一个数对应的二进制位应该都是这样的:

最终

代码

将这个思路写成代码:

    public boolean isValidSudoku(char[][] board) {
        // 首先需要有个数组来存每个数字的位,一共九个数
        int[] nums = new int[9];
        
        for(int i=0; i<9; i++) {
            for(int j=0; j<9; j++) {
                // 获取当前数字在行级上需要前移的位
                // 例:数字1,则无需移动 1 << 0
                // 对应的行的位置就是 000000001
                // 其它同理
                int value = board[i][j] - '1';
                if(value >= 0 && value <= 8) {
                    // 获取位置对应的位
                    int valueLocation = (1 << i) | (1 << (j+9)) | (1 << (i/3 + j/3*3 + 18));
                    
                    // 获取已经写好的位置,与运算判断是否有重复
                    int old = nums[value];
                    if((old & valueLocation) == 0) {
                        // 无重复,或运算加入nums
                        nums[value] = valueLocation | old;
                    } else {
                        return false;
                    }
                    
                }
            }
        }
        
        return true;
        
    }

都结合网上资料加上自己的一些理解,如果有影响到他人的地方,可以联系我:fzfz2007@163.com