数独详细实现过程

Posted 17岁boy想当攻城狮

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数独详细实现过程相关的知识,希望对你有一定的参考价值。

数独的游戏规则是:每一行每一列不能有出现重复数字!

其实用c语言实现也很简单,主要的就是思路!

这里我们编写两个函数,一个是用于生成,一个是用于解!

1.生成:

//生成数独
int** generate(int difficulty){
}

这里difficulty是表示每行要生成多少个数字,其返回值是一个二级指针,它指向一个二维数组

if (difficulty > 9){
		return 0;
	}
	//二维数组
	int **sudoku = NULL;

二维:

/*
	这里思路:
	先用二级指针指向9个连续指针空间的地址
	再用二级指针找到这个地址空间,然后每个地址空间在分配9个连续的int空间,这样就达到了二维的效果
	*/
	//申请一维
	sudoku = (int**)malloc(9 * sizeof(int*));
	//申请二维
	for (int i = 0; i < 9; ++i){
		sudoku[i] = (int*)malloc(9 * sizeof(int));
	}

初始化

for (int i = 0; i < 9; ++i){
		for (int j = 0; j < 9; ++j){
			*(sudoku+9*i+j) = 0;
		}
	}

这里的*(sudoku+9*i+j)其实就是解引用,找地址

sudoku解引用找到了第一个指针的地址,通过上面的分配第一个地址里的值指向一个连续的数组,大小为9

假如要找到第二维就要是sudoku第一个指针里的值+9=9,i为1则9*1=9.j=3则9+3=12.就找到了,第二个指针指向的数组里的第3个数

注意这里要说malloc地址不是连续的,第一个指针指向的地址相加不可能找到第二个指针里指向的值

要注意前面的解引用*

这里c语言也给我们做了相应的优化,当i值发生变化时,其实首先取得是第一个指针里的值,+9越界了之后呢,则会自动对第二个指针进行解引用,add递增的代码

这个是博主做测试得出的答案,若你感觉不对可以在下方留言

主要生成代码

int a = 0;	//随机数
	int b = 0; //随机插入的位置
	srand((unsigned)time(NULL));	//初始化随机数种子
	int c1 = 0;	//判断行生成是否达标
	int Num = 0;	//判断是否进入死数字
	for (int i = 0; i < 9; ++i){
		for (int j = 0; j < 9; ++j){
		t:
			if (Num == 10){
				//i = i - 2;	//退一行
				i = i-1;	//如果遇到死循环重新开始一次循环,注意其实还是对此行重新开始,因为-1后,循环结束还会+1
				Num = 0;
				c1 = 0;	//这里必须初始化为0,因为这里是判断是否达到标准,若不初始化为0会导致break以后每行出现0的问题(也就是判断达标了则不继续生成数字了,若去掉这行代码记得将break也去掉)
				/*
				for (int h = 0; h < 9; ++h){//当前行+1是因为上面-1
					Sudoku[i+1][h] = 0;
				}
				for (int h = 0; h < 9; ++h){//为了防止下一行被提前赋予值,可以将下一行也给0
				Sudoku[i+2][h] = 0;
				}
				*/
				break;	//在退一行的时候使用,避免出现卡死数字的情况,重新开始循环则不需要break,因为假如上一行没有重复数字则会进入死循环,所以需要上面的for循环将此行重新赋予值
			}
			a = rand() % 10;
			if (a == 0){
				goto t;
			}
			for (int k = 0; k < j; ++k){
				if (*((int*)sudoku + 9 * i + k) == a){
					goto t;
				}
			}
			for (int k = 0; k < i; ++k){
				if (*((int*)sudoku + 9 * k + j) == a){
					++Num;
					goto t;
				}
			}
			//随机插入,浪费时间,建议删除
			c:
			b = rand() % 9;
			if (*((int*)sudoku + 9 * i + 9) != 0){
				goto d;
			}
			if (*((int*)sudoku + 9 * i + b) != 0){
				goto c;
			}
			d:
			*((int*)sudoku + 9 * i + b) = a;
			//一直删除到这,然后把下面的代码取消注释
			//*((int*)sudoku + 9 * i + j) = a;
			++c1;
			Num = 0;
			//判断数字生成是否达标
			if (c1 == difficulty){
				c1 = 0;
				Num = 0;
				break;
			}
			
		}
	}
	getchar();
	return sudoku;

注释里写的很清楚,如果有什么不懂的地方可以在留言里提出来!

打印就非常简单了:

//打印数独
void PrintSukudo(int **sudoku){
	if (sudoku == NULL){
		return;
	}
	for (int i = 0; i < 9; ++i){
		for (int j = 0; j < 9; ++j){
			printf("%d,", *(sudoku + 9 * i + j));
		}
		printf("\\n");
	}
}

解数独:

其实生成写出来,解数独就很简单了

直接复用上面的代码就可以了。

//解数独
int Solution(int **sudoku){
	if (*sudoku == NULL){
		return NULL;
	}
	int a = 0;	//随机数
	int b = 0; //随机插入的位置
	srand((unsigned)time(NULL));	//初始化随机数种子
	int c1 = 0;	//判断行生成是否达标
	int Num = 0;	//判断是否进入死数字
	for (int i = 0; i < 9; ++i){
		for (int j = 0; j < 9; ++j){
		t:
			if (Num == 10){
				//i = i - 2;	//退一行
				i = i - 1;	//如果遇到死循环重新开始一次循环,注意其实还是对此行重新开始,因为-1后,循环结束还会+1
				Num = 0;
				c1 = 0;	//这里必须初始化为0,因为这里是判断是否达到标准,若不初始化为0会导致break以后每行出现0的问题(也就是判断达标了则不继续生成数字了,若去掉这行代码记得将break也去掉)
				/*
				for (int h = 0; h < 9; ++h){//当前行+1是因为上面-1
				Sudoku[i+1][h] = 0;
				}
				for (int h = 0; h < 9; ++h){//为了防止下一行被提前赋予值,可以将下一行也给0
				Sudoku[i+2][h] = 0;
				}
				*/
				break;	//在退一行的时候使用,避免出现卡死数字的情况,重新开始循环则不需要break,因为假如上一行没有重复数字则会进入死循环,所以需要上面的for循环将此行重新赋予值
			}
			a = rand() % 10;
			if (a == 0){
				goto t;
			}
			for (int k = 0; k < j; ++k){
				if (*((int*)sudoku + 9 * i + k) == a){
					goto t;
				}
			}
			for (int k = 0; k < i; ++k){
				if (*((int*)sudoku + 9 * k + j) == a){
					++Num;
					goto t;
				}
			}
			*((int*)sudoku + 9 * i + j) = a;
			//一直删除到这,然后把下面的代码取消注释
			//*((int*)sudoku + 9 * i + j) = a;
			Num = 0;
		

		}
	}
	return 1;

}

完整代码;

#include <time.h>
#include <stdlib.h>
//生成数独
int** generate(int difficulty){
	if (difficulty > 9){
		return 0;
	}
	//二维数组
	int **sudoku = NULL;
	/*
	这里思路:
	先用二级指针指向9个连续指针空间的地址
	再用二级指针找到这个地址空间,然后每个地址空间在分配9个连续的int空间,这样就达到了二维的效果
	*/
	//申请一维
	sudoku = (int**)malloc(9 * sizeof(int*));
	//申请二维
	for (int i = 0; i < 9; ++i){
		sudoku[i] = (int*)malloc(9 * sizeof(int));
	}
	//初始化
	/*
	这里的*(sudoku+9*i+j)其实就是解引用,找地址
	sudoku解引用找到了第一个指针的地址,通过上面的分配第一个地址里的值指向一个连续的数组,大小为9
	假如要找到第二维就要是sudoku第一个指针里的值+9=9,i为1则9*1=9.j=3则9+3=12.就找到了,第二个指针指向的数组里的第3个数
	注意这里要说malloc地址不是连续的,第一个指针指向的地址相加不可能找到第二个指针里指向的值
	要注意前面的解引用*
	这里c语言也给我们做了相应的优化,当i值发生变化时,其实首先取得是第一个指针里的值,+9越界了之后呢,则会自动对第二个指针进行解引用,add递增的代码
	这个是博主做测试得出的答案,若你感觉不对可以在下方留言
	*/
	for (int i = 0; i < 9; ++i){
		for (int j = 0; j < 9; ++j){
			*((int*)sudoku+9*i+j) = 0;
		}
	}
	
	int a = 0;	//随机数
	int b = 0; //随机插入的位置
	srand((unsigned)time(NULL));	//初始化随机数种子
	int c1 = 0;	//判断行生成是否达标
	int Num = 0;	//判断是否进入死数字
	for (int i = 0; i < 9; ++i){
		for (int j = 0; j < 9; ++j){
		t:
			if (Num == 10){
				//i = i - 2;	//退一行
				i = i-1;	//如果遇到死循环重新开始一次循环,注意其实还是对此行重新开始,因为-1后,循环结束还会+1
				Num = 0;
				c1 = 0;	//这里必须初始化为0,因为这里是判断是否达到标准,若不初始化为0会导致break以后每行出现0的问题(也就是判断达标了则不继续生成数字了,若去掉这行代码记得将break也去掉)
				/*
				for (int h = 0; h < 9; ++h){//当前行+1是因为上面-1
					Sudoku[i+1][h] = 0;
				}
				for (int h = 0; h < 9; ++h){//为了防止下一行被提前赋予值,可以将下一行也给0
				Sudoku[i+2][h] = 0;
				}
				*/
				break;	//在退一行的时候使用,避免出现卡死数字的情况,重新开始循环则不需要break,因为假如上一行没有重复数字则会进入死循环,所以需要上面的for循环将此行重新赋予值
			}
			a = rand() % 10;
			if (a == 0){
				goto t;
			}
			for (int k = 0; k < j; ++k){
				if (*((int*)sudoku + 9 * i + k) == a){
					goto t;
				}
			}
			for (int k = 0; k < i; ++k){
				if (*((int*)sudoku + 9 * k + j) == a){
					++Num;
					goto t;
				}
			}
			//随机插入,浪费时间,建议删除
			c:
			b = rand() % 9;
			if (*((int*)sudoku + 9 * i + 9) != 0){
				goto d;
			}
			if (*((int*)sudoku + 9 * i + b) != 0){
				goto c;
			}
			d:
			*((int*)sudoku + 9 * i + b) = a;
			//一直删除到这,然后把下面的代码取消注释
			//*((int*)sudoku + 9 * i + j) = a;
			++c1;
			Num = 0;
			//判断数字生成是否达标
			if (c1 == difficulty){
				c1 = 0;
				Num = 0;
				break;
			}
			
		}
	}
	return sudoku;
}
//打印数独
void PrintSukudo(int **sudoku){
	if (sudoku == NULL){
		return;
	}
	for (int i = 0; i < 9; ++i){
		for (int j = 0; j < 9; ++j){
			printf("%d,", *(sudoku + 9 * i + j));
		}
		printf("\\n");
	}
}
//解数独
int Solution(int **sudoku){
	if (*sudoku == NULL){
		return NULL;
	}
	int a = 0;	//随机数
	int b = 0; //随机插入的位置
	srand((unsigned)time(NULL));	//初始化随机数种子
	int c1 = 0;	//判断行生成是否达标
	int Num = 0;	//判断是否进入死数字
	for (int i = 0; i < 9; ++i){
		for (int j = 0; j < 9; ++j){
		t:
			if (Num == 10){
				//i = i - 2;	//退一行
				i = i - 1;	//如果遇到死循环重新开始一次循环,注意其实还是对此行重新开始,因为-1后,循环结束还会+1
				Num = 0;
				c1 = 0;	//这里必须初始化为0,因为这里是判断是否达到标准,若不初始化为0会导致break以后每行出现0的问题(也就是判断达标了则不继续生成数字了,若去掉这行代码记得将break也去掉)
				/*
				for (int h = 0; h < 9; ++h){//当前行+1是因为上面-1
				Sudoku[i+1][h] = 0;
				}
				for (int h = 0; h < 9; ++h){//为了防止下一行被提前赋予值,可以将下一行也给0
				Sudoku[i+2][h] = 0;
				}
				*/
				break;	//在退一行的时候使用,避免出现卡死数字的情况,重新开始循环则不需要break,因为假如上一行没有重复数字则会进入死循环,所以需要上面的for循环将此行重新赋予值
			}
			a = rand() % 10;
			if (a == 0){
				goto t;
			}
			for (int k = 0; k < j; ++k){
				if (*((int*)sudoku + 9 * i + k) == a){
					goto t;
				}
			}
			for (int k = 0; k < i; ++k){
				if (*((int*)sudoku + 9 * k + j) == a){
					++Num;
					goto t;
				}
			}
			*((int*)sudoku + 9 * i + j) = a;
			//一直删除到这,然后把下面的代码取消注释
			//*((int*)sudoku + 9 * i + j) = a;
			Num = 0;
		

		}
	}
	return 1;

}

int main(){
int **d = generate(5);
	Solution(d);
	PrintSukudo(d);
	getchar();
}

 

以上是关于数独详细实现过程的主要内容,如果未能解决你的问题,请参考以下文章

Android Studio实现数独小游戏,休闲益智

哪位大虾有九宫格(数独9*9)的C语言详细代码!!!!!!!!!!!!!!!!!!菜鸟在等候!!!!!

设计实现过程

数独 算法 C语言 代码

150+行Python代码实现带界面的数独游戏

150+行Python代码实现带界面的数独游戏!益智烧脑了解一下!