leetcode题解c++ | 37. Sudoku Solver

本文介绍了一种使用回溯法解决数独问题的算法,并提供了C++实现代码。通过贪心策略预先填充已确定的数字,减少搜索空间,进一步优化了算法效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目https://leetcode.com/problems/sudoku-solver/#/description

Write a program to solve a Sudoku puzzle by filling the empty cells.

Empty cells are indicated by the character '.'.

You may assume that there will be only one unique solution.


A sudoku puzzle...


...and its solution numbers marked in red.

分析

基本思路是回溯法。一个比较暴力的方法是,每次从1-9填,然后再判断。稍微好一点的方法是,用set记录每行每列每宫的未填值,然后每次从里面遍历,就不需要从1-9,时间大概在20+ms。更快一点的方法是(虽然我也不知为什么会快一点),用bool visit[81][10]来记录每个点的每个数是否可以访问,但回溯的时候不知道怎么撤销这个修改,所以只在初始的时候记录了一下,后面也是最暴力的,时间在10+ms。更更快的方法,是增加一个贪心,每次先确定当前已经可以确定的数,即当前已经可以排除8个数,时间在3ms。(代码有点凌乱)

c++实现

void pushToStack(int i, int num[], stack<int> &s,bool visit[][10], int number)
{
    ++num[i];
    visit[i][number] = 1;
    if(num[i]==8)
        s.push(i);
}

void update(int i, int j, int num[], stack<int> &s, vector< vector<char> >& board, bool visit[][10], int number)
{
    for(int k=0; k<9; ++k)
        if(board[i][k] == '.' && !visit[i*9+k][number])
            pushToStack(i*9+k, num, s, visit, number);
    for(int k=0; k<9; ++k)
        if(board[k][j] == '.' && !visit[k*9+j][number])
            pushToStack(k*9+j, num, s, visit, number);
    for(int x=0; x<3; ++x)
        for(int y=0; y<3; ++y)
            if(board[i/3*3+x][j/3*3+y] == '.' && !visit[(i/3*3+x)*9 + j/3*3+y][number])
                pushToStack( (i/3*3+x)*9 + j/3*3+y , num, s, visit, number);
}

void solveSudoku_greedy(vector< vector<char> >& board, bool visit[][10])
{
    //每次填已经可以确定的空
    int num[81] = {0};
    stack<int> s;

    for(int i=0; i<9; ++i)
        for(int j=0; j<9; ++j)
            if(board[i][j] != '.')
            {
                char c = board[i][j];
                int number = board[i][j] - '0';
                update(i, j, num, s, board, visit, board[i][j]-'0');
            }

    int tmp = s.size();
    while(!s.empty())
    {
        int sum = 0;
        int index = s.top();
        s.pop();
        int i=index/9, j=index%9;
        int number;
        bool visit_tmp[10] = {0};
        for(int k=0; k<9; ++k)
        {
            number = board[i][k]-'0';
            if(board[i][k] != '.' && !visit_tmp[number])
            {
                sum += number;
                visit_tmp[number]=1;
            }
        }
        for(int k=0; k<9; ++k)
        {
            number = board[k][j]-'0';
            if(board[k][j] != '.' && !visit_tmp[number])
            {
                sum += number;
                visit_tmp[number]=1;
            }
        }
        for(int x=0; x<3; ++x)
            for(int y=0; y<3; ++y)
            {
                number = board[i/3*3+x][j/3*3+y]-'0';
                if(board[i/3*3+x][j/3*3+y] != '.' && !visit_tmp[number])
                {
                    sum += number;
                    visit_tmp[number]=1;
                }
            }
        board[i][j] = (45-sum)+'0';
        update(i, j, num, s, board, visit, board[i][j]-'0');
    }
}

void onlyUpdate(int i, int j, vector< vector<char> >& board, bool visit[][10], int number)
{
    for(int k=0; k<9; ++k)
        visit[i*9+k][number] = 1;
    for(int k=0; k<9; ++k)
        visit[k*9+j][number] = 1;
    for(int x=0; x<3; ++x)
        for(int y=0; y<3; ++y)
            visit[(i/3*3+x)*9 + j/3*3+y][number] = 1;
}

bool check(int i, int j, vector< vector<char> > &board)
{
    for(int k=0; k<9; ++k)
        if(j!=k && board[i][k] == board[i][j])
            return false;
    for(int k=0; k<9; ++k)
        if(i!=k && board[k][j] == board[i][j])
            return false;
    for(int x=0; x<3; ++x)
        for(int y=0; y<3; ++y)
            if(i/3*3+x!=i && j/3*3+y!=j && board[i/3*3+x][j/3*3+y]==board[i][j])
                return false;
    return true;
}

bool backtracking(int pos, vector< vector<char> > &board, bool visit[][10])
{
    if(pos==81)
        return true;
    int i=pos/9, j=pos%9;
    if(board[i][j] == '.')
    {
        for(int k=1; k<=9; ++k)
            if(!visit[i*9+j][k])
        {
            board[i][j] = k+'0';
            if(check(i, j, board))
                if(backtracking(pos+1, board, visit))
                    return true;
            board[i][j] = '.';
        }
        return false;
    }
    else
        return backtracking(pos+1, board, visit);
}

void solveSudoku(vector< vector<char> >& board)
{
    bool visit[81][10] = {0};
    solveSudoku_greedy(board, visit);

    for(int i=0; i<9; ++i)
        for(int j=0; j<9; ++j)
            if(board[i][j] != '.')
                onlyUpdate(i, j, board, visit, board[i][j]-'0');
    backtracking(0, board, visit);
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值