一个使用回溯法,用C语言编写的求解数独的程序(学习交流)

这是我学习C语言半个月左右写的一个程序,可能有很多不合理之处,请大家指出.

数独按行输入,空格,换行符,字母等非数字不干扰输入,你只需要按照顺序一行一行的输入即可,空格假定为零.

#include <stdio.h>
#include <ctype.h>
void input_sudoku(int (*p_sudoku)[9]);
void output_sudoku(const int (*p_sudoku)[9]);
int check_rationality(const int (*p_sudoku)[9], int row, int column, int n);
int solve_sudoku(int (*p_sudoku)[9]);

int main(void)
{
    int sudoku[9][9];
    int state;
    input_sudoku(sudoku);
    output_sudoku(sudoku);
    state = solve_sudoku(sudoku); // 求解数独
    if (state)
    {
        printf("有解!\n");
        output_sudoku(sudoku);
    }
    else
    {
        printf("无解!\n");
    }
    // scanf(" %d ",&state);
    return 0;
}

void input_sudoku(int (*p_sudoku)[9]) // 输入数独
{
    char temp;
    printf("请输入数独:\n");
    for (int i = 0; i < 9; i++)
    {
        for (int j = 0; j < 9; j++)
        {
            while (!isdigit(temp = getchar())) // 检查输入是否是数字
            {
                continue;
            }
            p_sudoku[i][j] = (int)temp - '0'; // 字符转为数字
        }
        printf("第%d行已输入完毕.\n", i + 1);
    }
}

void output_sudoku(const int (*p_sudoku)[9]) // 输出数独
{
    printf("数独为:\n");
    for (int i = 0; i < 9; i++)
    {
        for (int j = 0; j < 9; j++)
        {
            printf("%d ", p_sudoku[i][j]);
        }
        printf("\n");
    }
}
int check_rationality(const int (*p_sudoku)[9], int row, int column, int num) // 检查该数字能否放在这个位置
{
    int i, j, center_row, center_column;
    for (j = 0; j < 9; j++) // 检查该数字是否会与该行重复
    {
        if (p_sudoku[row][j] == num)
        {
            return 0;
        }
    }
    for (i = 0; i < 9; i++) // 检查该数字是否会与该列重复
    {
        if (p_sudoku[i][column] == num)
        {
            return 0;
        }
    }
    center_row = row / 3 * 3;                      // 九宫左上角行索引
    center_column = column / 3 * 3;                // 九宫左上角列索引
    for (i = center_row; i <= center_row + 2; i++) // 检查是否与九宫中数字重复
    {
        for (j = center_column; j <= center_column + 2; j++)
        {
            if (p_sudoku[i][j] == num)
            {
                return 0;
            }
        }
    }
    return 1;
}
int solve_sudoku(int (*p_sudoku)[9])
{
    struct Idex // 定义结构体,用于存储空白格的信息
    {
        int row;
        int column;
        int state; // 该格起始候选数
    };
    struct Idex arr_idex[81]; // 供存放空白格索引
    int i, j;
    int end = -1, now = 0;  // end代表最后一个空格索引,now代表当前正在操作的空格
    for (i = 0; i < 9; i++) // 找到所有空格
    {
        for (j = 0; j < 9; j++)
        {
            if (p_sudoku[i][j] == 0)
            {
                end++;
                arr_idex[end].row = i;
                arr_idex[end].column = j;
                arr_idex[end].state = 1;
            }
        }
    }
    for (now = 0; now <= end;) // 循环求解
    {
        if (arr_idex[now].state > 9) // 如果候选数超出范围(情况发生在第K个格子填九,第k+1个格子无数可填时,需要连续回溯两次)
        {
            if (now == 0) // 这是起始空白格,说明该数独无解
            {
                return 0;
            }
            else // 不是起始空白格,再次回溯
            {
                arr_idex[now].state = 1;                               // 候选数复位
                p_sudoku[arr_idex[now].row][arr_idex[now].column] = 0; // 该格置为0(空格)
                now--;                                                 // 求解上一个空白格
                continue;
            }
        }

        for (i = arr_idex[now].state; i <= 9; i++) // 从起始候选数到9,依次遍历
        {
            if (check_rationality(p_sudoku, arr_idex[now].row, arr_idex[now].column, i)) // 该数字合理则填入
            {
                arr_idex[now].state = i + 1; // 更新起始候选数以供下一次使用
                p_sudoku[arr_idex[now].row][arr_idex[now].column] = i;
                now++; // 求解下一个单元格
                break;
            }
            else if (i == 9) // 该格已遍历完但没有合适的数
            {
                arr_idex[now].state = 1; // 回溯,同上
                p_sudoku[arr_idex[now].row][arr_idex[now].column] = 0;
                now--;
            }
        }
    }
    return 1; // 所有格子均已填完,即数独求解成功,返回1
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值