有效的数独

1.题目

请你判断一个 9 x 9 的数独是否有效。只需要 根据以下规则 ,验证已经填入的数字是否有效即可。

数字 1-9 在每一行只能出现一次。
数字 1-9 在每一列只能出现一次。
数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。(请参考示例图)
 

注意:

一个有效的数独(部分已被填充)不一定是可解的。
只需要根据以上规则,验证已经填入的数字是否有效即可。
空白格用 '.' 表示。

2.示例

提示:

  • board.length == 9
  • board[i].length == 9
  • board[i][j] 是一位数字(1-9)或者 '.'

3.分析:

此题如果想明白了,就非常简单,否则的话,只能用暴力算法破解,时间复杂度至少是O(n^2)。这里不介绍暴力破解算法,介绍一种比较常用的方法。

判断是否满足题中要求的数独,必须满足:

数字 1-9 在每一行只能出现一次。
数字 1-9 在每一列只能出现一次。
数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。

因此在判断的时候,可以分别判断行,列和每个3*3宫格。但是无论怎么判断都有一个规律,那就是每行、每列和每个3*3宫格最多只有9个数,而且大小都是1-9。

根据这个规律,我们可以创建一个数组temp[10]={0},用来把每行或没列或每个宫格的数字存在这个数组的对应位置上,例如:

假设某行的数字是:

["5","3",".",".","7",".",".",".","."]

第一个数字是5,那我们可以把这个数字保存在temp[5]=5;

第二个数字是3,那我们可以把这个数字保存在temp[3]=3;

第五个数字是7,那我们可以把这个数字保持在temp[7]=7;

那这样保存后,有什么用啦?那这里再举一个例子:

假设某行的数字是:

["5","3",".",".","7",".","5",".","."]

第一个数字是5,那我们可以把这个数字保存在temp[5]=5;

第二个数字是3,那我们可以把这个数字保存在temp[3]=3;

第五个数字是7,那我们可以把这个数字保持在temp[7]=7;

第七个数字是5,这个时候若还要把数字保存到temp数组中时,就会保存到temp[5]中,但是temp[5]中已经有数字5了,这个时候就可以判断这行数字不符合数独的特点,进而这个这个9*9数独是无效的。

同理,判断每列和每个3*3数独时是同样的道理。

4.代码:

bool isValidSudoku(char** board, int boardSize, int* boardColSize) {
	int temp[10] = { 0 };
	int just = 0;

	//判断每行,每轮循环都只判断一行数字
	for (int i = 0; i < 9; i++) //这个for代表行数
	{
		just = 0;//一个标志位,为0表示本行数字满足数独的特点,为1表示本行数字不符合数独的特点,因此就退出循环,直接表示9*9数独无效。
		for (int t = 0; t < 10; t++)temp[t] = 0;//每轮必须保证temp数组元素最开始都是1-9之外的数字,这里直接赋值为0
		for (int j = 0; j < 9; j++) //这个for表示这一行的所有元素
		{
			if (board[i][j] == '.')continue;
			if (temp[(board[i][j] - '0')] != 0) //发现temp数组对应下标的元素不为0了,表示先前已经被赋值,表示本行一定出现了2个相同的数字,此时9*9数独无效
			{
				just = 1;//标志位为0
				break;//跳出内层循环
			}
			//如果不为0,直接给temp数组的对应下标元素赋值——temp[元素值]=元素值
			temp[(board[i][j] - '0')] = (board[i][j] - '0');
		}
		if (just == 1)break;//判断内层循环结束后,也就是本轮循环检查结束后,该行是否满足数独的条件。如果为1,就不满足,直接跳出外层循环,不需要再检查其他行了,反之继续检查其他行。
	}
	if (just == 1)return false;//判断每行是否满足数独的条件。如果just==1,表示必有一行不满足数独的条件,直接返回false;反之进入每列的检查。

	//判断每列,原理与每行的检查完全一行,这里不赘述了。
	just = 0;
	for (int j = 0; j < 9; j++) 
	{
		just = 0;
		for (int t = 0; t < 10; t++)temp[t] = 0;
		for (int i = 0; i < 9; i++) 
		{
			if (board[i][j] == '.')continue;
			if (temp[(board[i][j] - '0')] != 0) 
			{
				just = 1;
				break;
			}
			temp[(board[i][j] - '0')] = (board[i][j] - '0');
		}
		if (just == 1)break;
	}
	if (just == 1)return false;//判断每列是否满足数独的条件。如果just==1,表示必有一行不满足数独的条件,直接返回false;反之进入每列的检查。


	//判断每个3*3宫内,这里的代码怎么写,有多种方式,并不唯一。
	just = 0;
	for (int i = 0; i < 3; i++)//这个for依旧代表行,表示每行有3个3*3数独
	{
		just = 0;//还是标志位,作用一样
		for (int j = 0; j < 3; j++)//这个for代表每列,表示每列有3个3*3数独
		{
			just = 0;//还是标志位,作用一样
			for (int t = 0; t < 10; t++)temp[t] = 0;//保证每次检查一个3*3数独时,temp数组的所有元素都要是0

			//这里是每个3*3数独的检查
			for (int m = i * 3; m <= i * 3 + 2; m++)//每个3*3数独的每行
			{
				for (int n = j * 3; n <= j * 3 + 2; n++)//每个3 * 3数独的每列
				{
					if (board[m][n] == '.')continue;
					if (temp[(board[m][n] - '0')] != 0)//与行检查时是一样的原理,不赘述
					{
						just = 1;
						break;
					}
					temp[(board[m][n] - '0')] = (board[m][n] - '0');
				}
				if (just == 1)break;
			}
			if (just == 1)break;
		}
		if (just == 1)break;
	}

	if (just == 1)return false;//判断每个3*3是否是满足数独的。若just=1,表示至少一个不满足,所以返回false

	return true;//能走到这里,表示这个9*9数独是有效的

}

5.总结

本题就在于思路的转换。最普通的想法就是在检查每行,每列和每个3*3时进行轮询。这样做的时间复杂度是非常大的,即使只检查一行,也要循环81次。但如果采用上面的思想,把所有行检查完,也才循环81次。

综上,本题并不复杂,多做题后,自然就会有这种思路。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值