没有对输入的待解数独进行一般性验证(同一行、一列以及同一个小九宫格都不能出现重复数字)
算法利用回溯的思想:
从第一个空白处开始,找到其候选解(排除同行、同列以及同一小九宫格的所有出现过的数字,剩下未出现的数字都是候选解)的第一个值填入数独。
对第二个空白执行第一步(前面所填入的数字对此空白处有影响)。
当出现某个空白的候选解个数为0时,就开始回溯,找到第一个候选解多于一个的,将其在使用的候选解设为不可取(本程序取值为-1),找到其下一个候选解,继续上面的步骤!
直到所有空白处填满,运算完成,输出结果!
#include <stdio.h>
#include <malloc.h>
typedef struct node
{
int col;
int row;
int value[10];
}Node;
int findvalue(int sudoku[9][9], Node * node);
int main(void)
{
int sudoku[9][9] = {{3, 4, 0, 0, 0, 2, 0, 7, 5},
{0, 5, 0, 0, 0, 0, 0, 4, 0},
{0, 0, 0, 8, 0, 0, 2, 0, 0},
{0, 0, 0, 6, 0, 0, 0, 9, 4},
{0, 0, 0, 2, 0, 9, 0, 0, 0},
{4, 9, 0, 0, 0, 8, 0, 0, 0},
{0, 0, 9, 0, 0, 7, 0, 0, 0},
{0, 3, 0, 0, 0, 0, 0, 5, 0},
{2, 7, 0, 9, 0, 0, 0, 1, 3}};
int i, j, k = 0, num_of_empty = 0;
int index, temp = 0;
//计算所给数独中待填入的空白数
for(i=0; i<9; i++)
for(j=0; j<9; j++)
if(sudoku[i][j]==0)
num_of_empty++;
//为回溯栈分配空间
Node * node_stack = (Node *)malloc(sizeof(struct node) * num_of_empty);
//回溯法求解数独
while(num_of_empty)
{
for(i=0; i<9; i++)
for(j=0; j<9; j++)
if(sudoku[i][j]==0)
{
//初始化栈中存储候选值的数组
for(index=0; index<10; index++)
(node_stack + k)->value[index] = 0;
(node_stack + k)->col = i;
(node_stack + k)->row = j;
sudoku[i][j] = findvalue(sudoku, node_stack + k);
if(sudoku[i][j]==-1)
{
sudoku[i][j] = 0;
k--;
while((node_stack + k)->value[0]==1)
{
sudoku[(node_stack + k)->col][(node_stack + k)->row] = 0;
num_of_empty++;
k--;
}
(node_stack + k)->value[0]--;
i = (node_stack + k)->col;
j = (node_stack + k)->row;
for(index=1; index<10; index++)
if((node_stack + k)->value[index]==0)
{
(node_stack + k)->value[index] = -1;
break;
}
for(index=1; index<10; index++)
if((node_stack + k)->value[index]==0)
{
sudoku[i][j] = index;
break;
}
}
k++;
}
//当栈空,说明数独错误,无解
if(k==0)
{
printf("此数独无解!\n");
free(node_stack);
return 0;
}
num_of_empty--;
}
free(node_stack);
//打印数独
for(i=0; i<9; i++)
{
for(j=0; j<9; j++)
printf("%2d ", sudoku[i][j]);
printf("\n");
}
return 0;
}
int findvalue(int sudoku[9][9], Node * node)
{
int m, n, i = node->col, j = node->row;
for(m=1; m<10; m++)
{
if(node->value[sudoku[i][m-1]]==0)
node->value[sudoku[i][m-1]] = sudoku[i][m-1];
if(node->value[sudoku[m-1][j]]==0)
node->value[sudoku[m-1][j]] = sudoku[m-1][j];
}
for(m=0; m<3; m++)
for(n=0; n<3; n++)
if(node->value[sudoku[i/3*3+m][j/3*3+n]]==0)
node->value[sudoku[i/3*3+m][j/3*3+n]] = sudoku[i/3*3+m][j/3*3+n];
for(m=1; m<10; m++)
if(node->value[m]==0)
node->value[0]++;
for(m=1; m<10; m++)
if(node->value[m]==0)
break;
if(node->value[0]==0)
return -1;
else
return m;
}
做了下改进:将各个步骤独立成函数,加入了待解数独的前期一般性检查,还有部分代码优化
#include <stdio.h>
#include <stdlib.h>
#define BOOL int
#define FALSE 1
#define TRUE 0
typedef struct node
{
int col;
int row;
int value[10];
} Node;
int findvalue(int sudoku[9][9], Node * node);
BOOL general_inspection(int sudoku[9][9]);
int blank_num(int sudoku[9][9]);
Node * mem_alloc(int num_of_empty);
void trace(int sudoku[9][9], Node * node_stack, int num_of_empty);
void print_sudoku(int sudoku[9][9]);
int main(void)
{
int sudoku[9][9] = {{3, 4, 1, 0, 0, 2, 0, 7, 5},
{0, 5, 0, 0, 0, 0, 0, 4, 0},
{0, 0, 0, 8, 0, 0, 2, 0, 0},
{0, 0, 0, 6, 0, 0, 0, 9, 4},
{0, 0, 0, 2, 0, 9, 0, 0, 0},
{4, 9, 0, 0, 0, 8, 0, 0, 0},
{0, 0, 9, 0, 0, 7, 0, 0, 0},
{0, 3, 0, 0, 0, 0, 0, 5, 0},
{2, 7, 0, 9, 0, 0, 0, 1, 3}};
int num_of_empty;
//为回溯栈分配空间
Node * node_stack;
if(general_inspection(sudoku))
{
printf("此数独存在错误!请检查\n");
print_sudoku(sudoku);
return 0;
}
num_of_empty = blank_num(sudoku);
node_stack = mem_alloc(num_of_empty);
trace(sudoku, node_stack, num_of_empty);
print_sudoku(sudoku);
return 0;
}
BOOL general_inspection(int sudoku[9][9])
{
int temp[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
int i, j, m, n;
for(i=0; i<9; i++)
for(j=0; j<9; j++)
if(sudoku[i][j]!=0)
{
//检查所在行
for(m=0; m<10; m++)
temp[m] = 0;
for(m=0; m<9; m++)
if(sudoku[i][m]!=0)
{
if(temp[sudoku[i][m]]==0)
temp[sudoku[i][m]] = 1;
else
return FALSE;
}
//检查所在列
for(m=0; m<10; m++)
temp[m] = 0;
for(m=0; m<9; m++)
if(sudoku[m][j]!=0)
{
if(temp[sudoku[m][j]]==0)
temp[sudoku[m][j]] = 1;
else
return FALSE;
}
//检查所在九宫格
for(m=0; m<10; m++)
temp[m] = 0;
for(m=0; m<3; m++)
for(n=0; n<3; n++)
if(sudoku[i/3*3+m][j/3*3+n]!=0)
{
if(temp[sudoku[i/3*3+m][j/3*3+n]]==0)
temp[sudoku[i/3*3+m][j/3*3+n]] = 1;
else
return FALSE;
}
}
return TRUE;
}
int blank_num(int sudoku[9][9])
{
//计算所给数独中待填入的空白数
int i, j, num = 0;
for(i=0; i<9; i++)
for(j=0; j<9; j++)
if(sudoku[i][j]==0)
num++;
return num;
}
Node * mem_alloc(int num_of_empty)
{
Node * node_stack = (Node *)malloc(sizeof(struct node) * num_of_empty);
if(node_stack==NULL)
{
printf("内存分配失败!\n");
exit(1);
}
return node_stack;
}
void trace(int sudoku[9][9], Node * node_stack, int num_of_empty)
{
int i, j, index, k = 0;
//回溯法求解数独
while(num_of_empty)
{
for(i=0; i<9; i++)
{
for(j=0; j<9; j++)
{
if(sudoku[i][j]==0)
{
(node_stack + k)->col = i;
(node_stack + k)->row = j;
sudoku[i][j] = findvalue(sudoku, node_stack+k);
if(sudoku[i][j]==-1)
{
sudoku[i][j] = 0;
k--;
while((node_stack + k)->value[0]==0)
{
//当栈空,说明数独错误,无解
if(k==0)
{
printf("此数独无解!\n");
//free(node_stack); //为啥这里一释放内存,就弹出debug assertion failed窗口啊!
exit(1);
}
sudoku[(node_stack + k)->col][(node_stack + k)->row] = 0;
num_of_empty++;
k--;
}
for(index=1; index<10; index++)
if((node_stack + k)->value[index]==0)
{
sudoku[(node_stack + k)->col][(node_stack + k)->row] = index;
(node_stack + k)->value[index] = 1;
(node_stack + k)->value[0]--;
break;
}
num_of_empty++;
i = (node_stack + k)->col;
j = (node_stack + k)->row;
}
k++;
num_of_empty--;
}
}
}
}
//栈空间使用结束,释放
free(node_stack);
node_stack=NULL;
}
int findvalue(int sudoku[9][9], Node * node)
{
int m, n, i = node->col, j = node->row;
//初始化栈中存储候选值的数组
for(m=0; m<10; m++)
node->value[m] = 0;
for(m=1; m<10; m++)
{
node->value[sudoku[i][m-1]] = 1;
node->value[sudoku[m-1][j]] = 1;
}
for(m=0; m<3; m++)
for(n=0; n<3; n++)
node->value[sudoku[i/3*3+m][j/3*3+n]] = 1;
//node->value[0]记录候选值个数,前面的循环可能会修改掉它,需要重新赋0值
node->value[0] = 0;
for(m=1; m<10; m++)
if(node->value[m]==0) node->value[0]++;
for(m=1; m<10; m++)
if(node->value[m]==0)
{
node->value[m] = 1;
node->value[0]--;
break;
}
//返回候选值m,若无候选值可用,返回错误标记-1
if(m==10)
return -1;
else
return m;
}
void print_sudoku(int sudoku[9][9])
{
//打印数独
int i, j;
for(i=0; i<9; i++)
{
for(j=0; j<9; j++)
printf("%2d ", sudoku[i][j]);
printf("\n");
}
}