LeetCode 刷题记录 36. Valid Sudoku

题目:
Determine if a 9x9 Sudoku board is valid. Only the filled cells need to be validated according to the following rules:

Each row must contain the digits 1-9 without repetition.
Each column must contain the digits 1-9 without repetition.
Each of the 9 3x3 sub-boxes of the grid must contain the digits 1-9 without repetition.
在这里插入图片描述
A partially filled sudoku which is valid.

The Sudoku board could be partially filled, where empty cells are filled with the character ‘.’.

Example 1:

Input:
[
[“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”]
]
Output: true
Example 2:

Input:
[
[“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”]
]
Output: false
Explanation: Same as Example 1, except with the 5 in the top left corner being
modified to 8. Since there are two 8’s in the top left 3x3 sub-box, it is invalid.
Note:

A Sudoku board (partially filled) could be valid but is not necessarily solvable.
Only the filled cells need to be validated according to the mentioned rules.
The given board contain only digits 1-9 and the character ‘.’.
The given board size is always 9x9.

解法1:
暴力法
对数独扫描三遍,分别检查行是否有效 列是否有效 和子块是否有效
判断有效我们可以判断每位数字是否重复出现,我们可以不用map而用数组的索引作为键,数组的值0表示未出现,1表示出现,当我们发现某位数字的值为1,说明它重复出现了,说明不是有效的,如果是0,将它的值赋1,说明是有效的
检查子块:
我们首先找到9个子块的左上角元素
(0,0) (0,3) (0,6)
(3,0) (3,3) (3,6)
(6,0) (6,3) (6,6)
然后在每个子块内部做一次3*3扫描
c++:

class Solution {
public:
    bool isValidSudoku(vector<vector<char>>& board) {
        int used[9];
        for(int i = 0; i < 9; i++){
            memset(used, 0, sizeof(used));
            for(int j = 0; j < 9; j++){
                if(!isValid(board[i][j], used)) return false;
            }
        }
        for(int j = 0; j < 9; j++){
            memset(used, 0, sizeof(used));
            for(int i = 0; i < 9; i++){
                if(!isValid(board[i][j], used)) return false;
            }
        }
        for(int r = 0; r < 9; r+=3){
            for(int c = 0; c < 9; c+=3){
                memset(used, 0, sizeof(used));
                for(int i = r; i < (r + 3); i++){
                    for(int j = c; j < (c + 3); j++)
                        if(!isValid(board[i][j], used)) return false;
                }
            }
        }
        return true;
    }
    bool isValid(char c, int used[9]){
        if(c == '.') return true;
        
        if(used[c-'0'-1] == 1) return false;
        used[c-'0'-1] = 1;
        return true;
        
    }
};

java:
数组置0操作:Arrays.fill(used, 0);

class Solution {
    public boolean isValidSudoku(char[][] board) {
        int[] used = new int[9];
        for(int i = 0; i < 9; i++){
            Arrays.fill(used, 0);
            for(int j = 0; j < 9; j++){
                if(!isValid(board[i][j], used)) return false;
            }
        }
        for(int j = 0; j < 9; j++){
             Arrays.fill(used, 0);
            for(int i = 0; i < 9; i++){
                if(!isValid(board[i][j], used)) return false;
            }
        }
        for(int r = 0; r < 9; r+=3){
            for(int c = 0; c < 9; c+=3){
                 Arrays.fill(used, 0);
                for(int i = r; i < (r + 3); i++){
                    for(int j = c; j < (c + 3); j++)
                        if(!isValid(board[i][j], used)) return false;
                }
            }
        }
        return true;
    }
    public boolean isValid(char c, int[] used){
        if(c == '.') return true;
        if(used[c-'0'-1] == 1) return false;
        used[c-'0'-1] = 1;
        return true;
        
    }
}

python:
判断有效的方法用到了set,去重后数组如果和原数组长度一样,说明有效
zip函数可以起到转置的作用
Python zip() 函数

class Solution(object):
    def isValidSudoku(self, board):
        """
        :type board: List[List[str]]
        :rtype: bool
        
        """
        for row in board:
            if not self.isValid(row):
                return False
        for col in zip(*board):
            if not self.isValid(col):
                return False
        for i in (0, 3, 6):
            for j in (0, 3, 6):
                square = [board[x][y] for x in range(i, i+3) for y in range(j, j+3) ]
                if not self.isValid(square):
                    return False
        return True
        
            
        
    def isValid(self, unit):
        unit = [i for i in unit if i != '.' ]
        return len(set(unit)) == len(unit)
       
        
    

解法2:
扫描三遍太费时间,我们可以扫描一遍来达到同样的效果
我们设置三个二维数组分别来记录每行、每列、每个子块的数字出现情况,初始化为0,表示未出现过
进行一次扫描,查看每个字符,查看在对应的行、列、子块的数字出现情况,如果为1,说明该数字出现过,该数独无效
如果为0,则将其值赋为1
行和列比较好判断,难点在于判断所在的子块,下面的图标明每个字符所在的子块
0 0 0 1 1 1 2 2 2
0 0 0 1 1 1 2 2 2
0 0 0 1 1 1 2 2 2
3 3 3 4 4 4 5 5 5
3 3 3 4 4 4 5 5 5
3 3 3 4 4 4 5 5 5
6 6 6 7 7 7 8 8 8
6 6 6 7 7 7 8 8 8
6 6 6 7 7 7 8 8 8
由此我可以得到 子块号num = (i / 3)* 3 + (j / 3)
c++:

class Solution {
public:
    bool isValidSudoku(vector<vector<char>>& board) {
        int rowUsed[9][9] = {0};
        int colUsed[9][9] = {0};
        int squareUsed[9][9] = {0};
        for(int i = 0; i < 9; i++){
            for(int j = 0; j < 9; j++){
                if(board[i][j] == '.' ) continue;
                int num = board[i][j] - '0' - 1;
                int k = (i/3) * 3 + (j/3);
                if(rowUsed[i][num] || colUsed[num][j] || squareUsed[k][num]) return false;
                rowUsed[i][num] = colUsed[num][j] = squareUsed[k][num] = 1;
                
               
            }
        }
        return true;
    }
        
       
    
};

java:
二维数组在生成时自动初始化为0

class Solution {
    public boolean isValidSudoku(char[][] board) {
        int[][] rowUsed = new int[9][9];
        int[][] colUsed = new int[9][9];
        int[][] squareUsed = new int[9][9];
        for(int i = 0; i < 9; i++){
            for(int j = 0; j < 9; j++){
                if(board[i][j] == '.' ) continue;
                int num = board[i][j] - '0' - 1;
                int k = (i/3) * 3 + (j/3);
                if(rowUsed[i][num] == 1 || colUsed[num][j] == 1 || squareUsed[k][num] == 1) return false;
                rowUsed[i][num] = colUsed[num][j] = squareUsed[k][num] = 1;
                
               
            }
        }
        return true;
    }
}

python:
注意二维数组的生成方式
另外不能写成 num = board[i][j] - ‘0’ - 1
会出现unsupported operand type(s) for -: ‘unicode’ and ‘str’

class Solution(object):
    def isValidSudoku(self, board):
        """
        :type board: List[List[str]]
        :rtype: bool
        
        """
        rowUsed = [[0 for i in range(9)]  for j in range(9)]
        colUsed = [[0 for i in range(9)]  for j in range(9)]
        squareUsed = [[0 for i in range(9)]  for j in range(9)]
        for i in xrange(9):
            for j in xrange(9):
                if board[i][j] == '.': continue
                num = int(board[i][j])  - 1
                k = (i/3) * 3 + (j/3);
                if rowUsed[i][num] or colUsed[num][j] or squareUsed[k][num]: return False
                rowUsed[i][num] = colUsed[num][j] = squareUsed[k][num] = 1
                

        return True
        

解法3:
用三个二维数组标识行、列和子块太费空间,我们可以用一个set来储存所有的信息,为了区分是不同行列子块,我们需要加标识信息

 string row = to_string(board[i][j]) + " in row " + to_string(i);
 string col = to_string(board[i][j]) + " in col " + to_string(j);
 string block = to_string(board[i][j]) + " in block " + to_string(k);

这样我们就可以有效区分一个数字是行、列还是子块,此外它在行、列和子块内部中的位置
我们在一次遍历的时候,每取到一个数字,先生成它的行、列和子块的相关信息,看在set中是否有重复的,如果有说明数独无效
没有则插入set
c++:
拼接字符串要将字符和数字用 to_string函数转成字符串
判断是否重复有两种方法:

  1. count函数判断是否重复,然后insert函数将信息插入set中
  2. 直接insert函数将信息插入set中,如果有重复则无法插入,根据返回值来判断是否有重复
    返回值为pair<set::iterator, bool>,我们取st.insert(row).second来判断重复
class Solution {
public:
    bool isValidSudoku(vector<vector<char>>& board) {
        unordered_set<string> st;
        for(int i = 0; i < 9; i++){
            for(int j = 0; j < 9; j++){
                if(board[i][j] == '.' ) continue;
                int k = (i/3) * 3 + (j/3);
                string row = to_string(board[i][j]) + " in row " + to_string(i);
                string col = to_string(board[i][j]) + " in col " + to_string(j);
                string block = to_string(board[i][j]) + " in block " + to_string(k);
                if(!st.insert(row).second || !st.insert(col).second || !st.insert(block).second) return false;
                //if (st.count(row) || st.count(col) || st.count(block)) return false;
                // st.insert(row);
                // st.insert(col);
                // st.insert(block);
                
               
            }
        }
        return true;
    }
        
       
    
};

java:
拼接字符串要将字符和数字可以直接与字符串相加,内部新生成StringBuilder对象,将数字加进去,然后返回toString()
为什么java中string类型数据和int类型数据相加为string类型数据?
判断是否重复有两种方法:

  1. contains函数判断是否重复,然后add函数将信息插入set中
  2. 直接add函数将信息插入set中,如果有重复则无法插入,根据返回值来判断是否有重复
    无法插入返回false
class Solution {
    public boolean isValidSudoku(char[][] board) {
        Set<String> st = new HashSet<>();
        for(int i = 0; i < 9; i++){
            for(int j = 0; j < 9; j++){
                if(board[i][j] == '.' ) continue;
                int k = (i/3) * 3 + (j/3);
                String row = "(" + board[i][j] + ")" + i;
                String col = j + "(" + board[i][j] + ")";
                String block = (i/3) + "(" + board[i][j] + ")" + (j/3);
                // if(st.contains(row) || st.contains(col) || st.contains(block)) return false;
                // st.add(row);
                // st.add(col);
                // st.add(block);
                if(!st.add(row) || !st.add(col) || !st.add(block)) return false;
               
            }
        }
        return true;
    }
}

python:
拼接字符串要先将数字转成str再拼接
set中的add方法没有返回值,所以我们只能用in 判断重复元素是否存在

class Solution(object):
    def isValidSudoku(self, board):
        """
        :type board: List[List[str]]
        :rtype: bool
        
        """
        st = set()
        for i in xrange(9):
            for j in xrange(9):
                if board[i][j] == '.': continue
                row = "(" + board[i][j] + ")" + str(i)
                col = str(j) + "(" + board[i][j] + ")"
                block = str(i/3) + "(" + board[i][j] + ")" + str(j/3)
                
                if row in st or col in st or block in st: return False
                st.add(row)
                st.add(col)
                st.add(block)
                

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值