LeetCode刷题系列---36. 有效的数独

LeetCode刷题系列---36. 有效的数独

  • 题目
    • 示例
  • 解题
    • 解决思路1
      • 本地代码
      • 提交代码
    • 解决思路2
      • 本地代码
      • 提交代码
    • 别人的题解

题目

一个9x9的数字矩阵,每个格子可以是1-9的整数,每行每列的数值不重复,将9x9的矩阵分成3x3个九宫格,每个九宫格的数值也不重复。就认为是一个有效的数独。
只要符合上述规则即为有效,不用考虑数独是否可解。
输出的字符串,空格用点号代替。
刷题请直接到有效的数独

示例

输入

[“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

解释
LeetCode刷题系列---36. 有效的数独_第1张图片
输入对应上表,符合有效的规则

解题

本来我打算先把字符数组转存为int数组,然后再对int数组操作。后来发现没有必要。需要用的话再转换就可以。

解决思路1

暴力求解。
每行、每列、每个九宫格分别判断。
判断时,创建一个bool数组,遍历当下需要判断的9个数,如果出现过就标记为true,如果读到的数被标记过,那么该数出现过,说明该数独无效。
时间复杂度O(n^3)

本地代码

#include 
#include 

char board[10][10];

struct myList{
	int value[9];
	myList(){
		memset(value, 0, sizeof(value));
	}
};

bool check(myList l){
	bool isAppeared[10];
	memset(isAppeared, 0, sizeof(isAppeared));
	
	for(int i=0; i<9; i++){
		while(i<9 && l.value[i] == 0)  i++;
		if(i == 9) break;
//		printf("%d \n",isAppeared[l.value[i]]);
		if(isAppeared[l.value[i]] == true)
			return false;
		isAppeared[l.value[i]] = true;
	}
	return true;
}

int char2int(char c){
	if(c == '.') return 0;
	return (int)c-(int)'0';
}

bool isValidSudoku(){
	int n = 9;
	
	//chack by row
	for(int i=0; i<n; i++){
		myList l;
		for(int j=0; j<n; j++)
			l.value[j] = char2int(board[i][j]);

		if(!check(l)) return false;
	}
	
	//chack by col
	for(int i=0; i<n; i++){
		myList l;
		for(int j=0; j<n; j++)
			l.value[j] = char2int(board[j][i]);
		if(!check(l)) return false;
	}
	
	//chack by 3*3
	for(int i=0; i<3; i++){
		for(int j=0; j<3; j++){
			myList l;
			int tmp = 0;
			for(int a=0; a<3; a++)
				for(int b=0; b<3; b++)
					l.value[tmp++] = char2int(board[i*3+a][j*3+b]);			
			if(!check(l)) return false;
		}			
	}
	return true;
}

int main(){
//53..7....
//6..195...
//.98....6.
//8...6...3
//4..8.3..1
//7...2...6
//.6....28.
//...419..5
//....8..79
	for(int a=0; a<9; a++)
		scanf("%s", board[a]);
//	for(int a=0; a<9; a++)
//		printf("%s\n", board[a]);

	printf("%d", isValidSudoku());
	
	return 0;
}
  1. 把每行、每列、每个九宫格的九个数构造一个结构体,是因为将数组作为参数容易出错。(其实是自己没有学好【手动捂脸】)
  2. char强制换位为int时,得到的是对应字符从ASCII码,例如‘1’转换出来是49,要想得到正确的对应数值,需要减去字符‘0’。
  3. check函数中的isAppeared数组,希望记录下标对应的数值有没有出现过。而数独表中的值是1-9,所以isAppeared大小至少要10,才能在访问isAppeared[9]时不报错。

提交代码

其实跟本地代码没什么差别

struct myList{
	int value[9];
	myList(){
		memset(value, 0, sizeof(value));
	}
};


bool check(myList l){
	bool isAppeared[10];
	memset(isAppeared, 0, sizeof(isAppeared));
	
	for(int i=0; i<9; i++){
		while(i<9 && l.value[i] == 0)  i++;
		if(i == 9) break;
//		printf("%d \n",isAppeared[l.value[i]]);
		if(isAppeared[l.value[i]] == true)
			return false;
		isAppeared[l.value[i]] = true;
	}
	return true;
}

int char2int(char c){
	if(c == '.') return 0;
	return (int)c-(int)'0';
}

class Solution {
public:
    bool isValidSudoku(vector<vector<char>>& board) {
        int n = 9;
	
	//chack by row
	for(int i=0; i<n; i++){
		myList l;
		for(int j=0; j<n; j++)
			l.value[j] = char2int(board[i][j]);

		if(!check(l)) return false;
	}
	
	//chack by col
	for(int i=0; i<n; i++){
		myList l;
		for(int j=0; j<n; j++)
			l.value[j] = char2int(board[j][i]);
		if(!check(l)) return false;
	}
	
	//chack by 3*3
	for(int i=0; i<3; i++){
		for(int j=0; j<3; j++){
			myList l;
			int tmp = 0;
			for(int a=0; a<3; a++)
				for(int b=0; b<3; b++)
					l.value[tmp++] = char2int(board[i*3+a][j*3+b]);			
			if(!check(l)) return false;
		}			
	}
	return true;
    }
};
  1. 提交报错Error: reference to 'list' is ambiguous
    原因是我声明了一个结构体叫list,而c++本来有一个list类。
    参考Error: reference to ‘list’ is ambiguous

  2. 提交时还遇到总是报错isAppeared数组下标总是超界
    原因是读取l.value[i]对应的isAppeared值,一开始结构体myList没写构造函数,也就是没有初始化,里边的值可能是任意值,所以会出现isAppeared下标超界。

解决思路2

空间换时间。应该可以在O(n^2)内解决。
声明9(行)+9(列)+9(九宫格)个大小为9的标记数组,遍历原始数组,直接在标记数组中操作,如果出现冲突(被标记过)就直接返回false。

本地代码

#include 
#include 

char board[10][10];
bool rAppeared[9][10], cAppeared[9][10], nAppeared[9][10];


bool isValidSudoku(){
	int n = 9;
	for(int i=0; i<n; i++){
		for(int j=0; j<n; j++){
			if(board[i][j]!='.'){
				int t = board[i][j] - '0';
				if(rAppeared[i][t] !=0 ) return false;
				else rAppeared[i][t] = 1;
				if(cAppeared[j][t] !=0 ) return false;
				else cAppeared[j][t] = 1;
				int n = (i/3)*3+(j/3);
				if(nAppeared[n][t] != 0) return false;
				else nAppeared[n][t] = 1;
			}
		}
	}
	return true;
}

int main(){
	for(int a=0; a<9; a++)
		scanf("%s", board[a]);

	printf("%d", isValidSudoku());	
	
	return 0;
}

提交代码

bool rAppeared[9][10], cAppeared[9][10], nAppeared[9][10];

class Solution {
public:
    bool isValidSudoku(vector<vector<char>>& board) {
        memset(rAppeared, 0 , sizeof(rAppeared));
        memset(cAppeared, 0 , sizeof(cAppeared));
        memset(nAppeared, 0 , sizeof(nAppeared));
        int n = 9;
		for(int i=0; i<n; i++){
			for(int j=0; j<n; j++){
				if(board[i][j]!='.'){
					int t = board[i][j] - '0';
					if(rAppeared[i][t] !=0 ) return false;
					else rAppeared[i][t] = 1;
					if(cAppeared[j][t] !=0 ) return false;
					else cAppeared[j][t] = 1;
					int n = (i/3)*3+(j/3);
					if(nAppeared[n][t] != 0) return false;
					else nAppeared[n][t] = 1;
				}
			}
		}
		return true;
    }
};

提交过程中出现同一组测试数据,期望结果是true,本地结果是true,但提交结果是false的情况。原因是本地代码中三个标记数组rAppeared、cAppeared和nAppeared是全局变量,每个元素都会被自动初始化为0。但是提交代码中没有自动初始化,也没有手动初始化,导致结果错误。

别人的题解

看了别人的题解,发现大家的做法和我的解决思路2一致,都是一次遍历整个数独矩阵,确认当前元素是否在当前行、当前列、当前九宫格出现过,如果出现过直接返回false,如果没有出现过则标记为已经出现过。
但是,别人的代码比我写的优雅。复制一个Java版的,供自己学习。

class Solution {
    public boolean isValidSudoku(char[][] board) {
        // 记录某行,某位数字是否已经被摆放
        boolean[][] row = new boolean[9][9];
        // 记录某列,某位数字是否已经被摆放
        boolean[][] col = new boolean[9][9];
        // 记录某 3x3 宫格内,某位数字是否已经被摆放
        boolean[][] block = new boolean[9][9];

        for (int i = 0; i < 9; i++) {
            for (int j = 0; j < 9; j++) {
                if (board[i][j] != '.') {
                    int num = board[i][j] - '1';
                    int blockIndex = i / 3 * 3 + j / 3;
                    if (row[i][num] || col[j][num] || block[blockIndex][num]) {
                        return false;
                    } else {
                        row[i][num] = true;
                        col[j][num] = true;
                        block[blockIndex][num] = true;
                    }
                }
            }
        }
        return true;
    }
}

参考官方题解

你可能感兴趣的:(LeetCode刷题)