数独详细实现过程
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();
}
以上是关于数独详细实现过程的主要内容,如果未能解决你的问题,请参考以下文章