[LeetCode 36]有效的数独

LeetCode 36 有效的数独

  • 题目描述
    • 题目分析
    • 源码
    • 难点
    • 小结
    • 改进
    • 改进代码
    • 分析

题目描述

[LeetCode 36]有效的数独_第1张图片
示例 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,col,block,来存储每一行,每一列以及每个九宫格内是否存在相同的数,如果有一个不符合要求,那么就返回false。
明确了思路就要动手写代码了,创建三个二维数组没什么难的有9行9列9个9宫格,那就[9][]。那么后面要保存什么呢,当然是出现的数字了1-9啦,但是为了让数字和索引相匹配吧,让我们强行让它从0开始,填上[9][10]。

源码

class Solution {
public boolean isValidSudoku(char[][] board) {
//创建二维数组,9个组块,10个数字记录数独中的数字
    boolean[][] row=new boolean[9][10]
    boolean[][] col=new boolean[9][10];
    boolean[][] block=new boolean[9][10];
    
    for(int i=0;i<9;i++){
        for(int j=0;j<9;j++){
            if(board[i][j]!='.'){
            //遍历到非空的字段
                int num=board[i][j]-'0';
                //取出数字保存到对应的位置,出现过记为true
                if(row[i][num]||col[j][num]||block[i/3*3+j/3][num]){
                    return false;
                }else{
                    row[i][num]=true;
                    col[j][num]=true;
                    block[i/3*3+j/3][num]=true;
                }
            }
        }
    }
    return true;
}
}

难点

对于我来说,难点主要是两个。
第一是将数独中的char转换为int,这个可以借助公式board[i][j]-‘0’。这个公式计算了这个字 符与0之间的差值并以整数的形式保存在0-9的数组中。
第二个是9宫格地址的保存,我们想将(0,0)记为0、(0,1)记为1、(0,2)记为2、(1,0)记为3…… 那么就需要推导一个公式来计算保存。因为9宫格是有三行,以row/3得到行数,再乘3是因为3个为一组。列数就很好理解了,我分成了三组,那么每组的组号不就是小组编号+列数编号嘛!

小结

以创建数组的形式保存各个出现的数字,然后来比对是否出现两次,第一次出现遍历一遍将默认值false改为true,第二次索引到直接跳出,因为这个数已经存在了,那么不管是行列还是九宫格中再出现,那都是不符合数独定义的。这个方法很好理解,毕竟是要便利整个九宫格嘛,那么O(n^2)的复杂度还是需要的。

改进

话虽是这么说,但是改进还是需要改进的,毕竟数组这东西没有那么高大上,创建三个数组再看也有点太麻烦了吧。那么有没有什么办法既能储存也能判断是否存在呢?肯定是有的!不然数据结构存在的意义在哪里,LinkedList这么好的东西,如果(!contains)那我就set,如果(contains)那就false,这种结构比一次次遍历数组好太多了吧!

改进代码

class Solution {
public boolean isValidSudoku(char[][] board) {
//创建链表保存要检验的行列九宫格数
    List> rows = new ArrayList<>(9);
    List> cols = new ArrayList<>(9);
    List> blocks = new ArrayList<>(9);
   //在链表中插入哈希表,为了保存检验已存在的数字
    for (int j = 0; j < 9; j++) {
        rows.add(new HashSet(9));
        cols.add(new HashSet(9));
        blocks.add(new HashSet(9));
    }
    
   for (int i = 0; i < 9; i++) {
        for (int j = 0; j < 9; j++) {
            if (board[i][j]!= '.'){
            //如果任意一个不能加那就是已经存在数据了,那么就返回false
            boolean row = rows.get(i).add(board[i][j]);
            boolean col = cols.get(j).add(board[i][j]);
            boolean block = blocks.get((i / 3) * 3 + (j / 3)).add(board[i][j]);
            if(row==false||col==false||block==false){
            return false;
            }
            }
        }
    }
    return true;
}
}

分析

当然,这个的时间复杂度还是O(n^2)因为需要两个循环来遍历所有的数字。

[1]https://leetcode-cn.com/problems/valid-sudoku/comments/

你可能感兴趣的:([LeetCode 36]有效的数独)