zl程序教程

您现在的位置是:首页 >  其他

当前栏目

leetcode 36. 有效的数独

2023-03-14 22:53:00 时间

有效的数独题解集合


数组法

解题思路

1.由于board中的整数限定在1到9的范围内,因此可以分别建立数组来存储任一个数在相应维度上是否出现过。维度有3个:所在的行,所在的列,所在的box,注意box的下标也是从左往右、从上往下的。

2.遍历到每个数的时候,例如boar[i][j],我们判断其是否满足三个条件:

  • 在第 i 个行中是否出现过
  • 在第 j 个列中是否出现过
  • 在第 j/3 + (i/3)*3个box中是否出现过.为什么是j/3 + (i/3)*3呢?

3.关于从数组下标到box序号的变换

重述一遍问题:给定i和j,如何判定board[i][j]在第几个box呢?

显然属于第几个box由i和j的组合唯一确定,例如board[2][2]一定是第0个box,board[4][7]一定是第5个box,可以画出来看一下,但是规律在哪里呢? 我们可以考虑一种简单的情况: 一个3x9的矩阵,被分成3个3x3的box,如图:

  • 显然每个数属于哪个box就只取决于纵坐标,纵坐标为0/1/2的都属于box[0],纵坐标为3/4/5的都属于box[1],纵坐标为6/7/8的都属于box[2].也就是j/3.
  • 而对于9x9的矩阵,我们光根据j/3得到0/1/2还是不够的,可能加上一个3的倍数,例如加0x3,表示本行的box,加1x3,表示在下一行的box,加2x3,表示在下两行的box, 这里的0/1/2怎么来的?和j/3差不多同理,也就是i/3。

简单来说,每一行有三个区域,要确定当前位置属于哪个区域,首先可以按列看,因为每三列组成一个区域,而一行共九列,我们只需要把当前位置的列表j/3,得到的值就是按列看所在的区域的列坐标(这里可以把一个区域细化为3*3矩阵中的一个位置)。

而按行看,一个区域占据三行,并且一行穿过三个区域,要计算当前区域在33矩阵中的行数,只需要用i/3即可,但是这里的行数是0行,3行和6行,多了三的倍数,因此这里还要3,即(i/3)*3

最后要确定当前位置所在区域,只需要j/3+(i/3)*3

代码:

class Solution {
public:
	bool isValidSudoku(vector<vector<char>>& board) 
	{
		//一开始数组里面元素都初始化为0,表示默认初始情况下,每一行每一个数都没有出现过,同理每一列,每一块区域都是如此
		int row[9][9] = {0};//记录每个数字分别出现在第几行
		int col[9][9] = {0};//记录每个数字分别出现在第几列
		int box[9][9] = {0};//记录每个数字分别出现在第几个区域
		for (int i = 0; i < 9; i++)
		{
			for (int j = 0; j < 9; j++)
			{
				   // 遍历到第i行第j列的那个数,我们要判断这个数在其所在的行有没有出现过,
                // 同时判断这个数在其所在的列有没有出现过
                // 同时判断这个数在其所在的box中有没有出现过
				if (board[i][j] == '.') continue;//如果当前位置是空白,就跳过,看下一个位置的有效数字
				int curNum = board[i][j] - '0';//字符转整数的方式,-'0'
				if (row[i][curNum-1]) return false;
				if (col[j][curNum-1]) return false;
				if (box[j / 3 + (i / 3) * 3][curNum-1]) return false;

				// 之前都没出现过,现在出现了,就给它置为1,下次再遇见就能够直接返回false了。
				row[i][curNum-1] = 1;
				col[j][curNum-1] = 1;
				box[j / 3 + (i / 3) * 3][curNum-1] = 1;
			}
		}
		return true;
	}
};

哈希法

由于只要我们判断是否为有效的数独。

所以我们只需要对 board 中出现的数进行判断,如果 board 中有数违反了数独的规则,返回 false,否则返回 true。

直观上,我们很容易想到使用哈希表来记录某行/某列/某个小方块出现过哪些数字,来帮助我们判断是否符合「有效数独」的定义。

这道题唯一的难点可能是在于如何确定某个数落在哪个小方块中,我们可以去小方块进行编号:

然后推导出小方块编号和行列的关系为: idx = i / 3 * 3 + j / 3。

其实就是把数组法里面三个用来保存位置判断重复的数组替换成哈希表罢了

代码:

class Solution {
public:
	bool isValidSudoku(vector<vector<char>>& board) 
	{
		unordered_map<int, unordered_set<int>> row,col,box;

		for (int i = 0; i < 9; i++)
		{
			for (int j = 0; j < 9; j++)
			{
				//判断当前i行j的数字有没有在i行,j列,或者当前所在的box出现过
				if (board[i][j] == '.') continue;//如果当前位置是空白,就跳过,看下一个位置的有效数字
				int curNum = board[i][j] - '0';//字符转整数的方式
				if (row[i].find(curNum)!=row[i].end()) return false;//判断当前行是否出现过当前数字curNum
				if (col[j].find(curNum)!= col[j].end()) return false;//判断当前列是否出现过当前数字curNum
				if (box[j / 3 + (i / 3) * 3].find(curNum)!= box[j / 3 + (i / 3) * 3].end()) return false;//判断当前区域是否出现过当前数字curNum

				row[i].insert(curNum);//设置当前行已经出现过了数字curNum
				col[j].insert(curNum);//设置当前列已经出现过了数字curNum
				box[j / 3 + (i / 3) * 3].insert(curNum);//设置当前区域已经出现过了数字curNum
			}
		}
		return true;
	}
};