[LeetCode] Sudoku Solver

203 篇文章 0 订阅

Sudoku Solver

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.

解题思路:

之前没有玩过数独,于是就上网恶补了一下。于是我就陷入了一个误区,数独的几种技巧想用程序完全实现出来。于是乎,纠结了很久。本来用程序员思维来做非常容易的,就是回溯而已。我实现了唯一余数法和行列摒除法,若不能继续求解,则用暴力法。代码如下。57ms。

class Solution {
public:
    void solveSudoku(vector<vector<char> > &board) {
        set<int> allCan({ 1, 2, 3, 4, 5, 6, 7, 8, 9 });
        map<int, set<int>> candidates;
        map<int, set<int>> rowLeft;       //某一行还剩下哪些数
        map<int, set<int>> colLeft;       //某一列还剩下哪些数
 
        for (int i = 0; i < 9; i++){
            rowLeft[i] = allCan;
            colLeft[i] = allCan;
        }
 
        for (int i = 0; i<9; i++){
            for (int j = 0; j<9; j++){
                if (board[i][j] == '.'){
                    candidates[get1D(i, j)] = allCan;
                    prunning(board, i, j, candidates);
                }
                else{
                    rowLeft[i].erase(board[i][j]-'0');
                    colLeft[j].erase(board[i][j] - '0');
                }
            }
        }
        while (candidates.size()>0){
            int unknowNumber = candidates.size();
            //唯一余数法
            {
                for (map<int, set<int>>::iterator it = candidates.begin(); it != candidates.end();){
                    if (it->second.size() == 1){
                        int i = get2Di(it->first);
                        int j = get2Dj(it->first);
                        board[i][j] = *(it->second.begin()) + '0';
                        rowLeft[i].erase(board[i][j] - '0');
                        colLeft[j].erase(board[i][j] - '0');
                        candidates.erase(it++);
                    }
                    else{
                        it++;
                    }
                }
                for (int i = 0; i<9; i++){
                    for (int j = 0; j<9; j++){
                        if (board[i][j] == '.'){
                            prunning(board, i, j, candidates);
                        }
                    }
                }
            }
 
 
            //行摒除法
            {
                for (int i = 0; i < 9; i++){
                    for (set<int>::iterator it = rowLeft[i].begin(); it != rowLeft[i].end();){
                        int pos = -1;
                        bool onlyOne = true;
                        for (int j = 0; j < 9; j++){
                            int index = get1D(i, j);
                            if (candidates.find(index) != candidates.end() && candidates[index].find(*it) != candidates[index].end()){
                                if (pos >= 0){
                                    onlyOne = false;
                                    break;
                                }
                                pos = j;
                            }
                        }
                        if (onlyOne&&pos >= 0){
                            board[i][pos] = *it + '0';
                            colLeft[pos].erase(*it);
                            rowLeft[i].erase(it++);
                            candidates.erase(get1D(i, pos));
                        }
                        else{
                            it++;
                        }
                    }
                }
                for (int i = 0; i < 9; i++){
                    for (int j = 0; j < 9; j++){
                        if (board[i][j] == '.'){
                            prunning(board, i, j, candidates);
                        }
                    }
                }
            }
 
 
            //列摒除法
            {
                for (int j = 0; j < 9; j++){
                    for (set<int>::iterator it = colLeft[j].begin(); it != colLeft[j].end();){
                        int pos = -1;
                        bool onlyOne = true;
                        for (int i = 0; i < 9; i++){
                            int index = get1D(i, j);
                            if (candidates.find(index) != candidates.end() && candidates[index].find(*it) != candidates[index].end()){
                                if (pos >= 0){
                                    onlyOne = false;
                                    break;
                                }
                                pos = i;
                            }
                        }
                        if (onlyOne&&pos >= 0){
                            board[pos][j] = *it + '0';
                            rowLeft[pos].erase(*it);
                            colLeft[j].erase(it++);
                            candidates.erase(get1D(pos, j));
                        }
                        else{
                            it++;
                        }
                    }
                }
                for (int i = 0; i<9; i++){
                    for (int j = 0; j<9; j++){
                        if (board[i][j] == '.'){
                            prunning(board, i, j, candidates);
                        }
                    }
                }
            }
            if (unknowNumber == candidates.size()){
                break;
            }
        }
 
        //若仍未解答出来,则暴力枚举
        if (candidates.size() > 0){
            dfsCheck(board, candidates, candidates.begin());
        }
    }
 
    bool dfsCheck(vector<vector<char> > &board, map<int, set<int>> &candidates, map<int, set<int>>::iterator it){
        if (it == candidates.end()){
            return true;
        }
        int i = get2Di(it->first);
        int j = get2Dj(it->first);
        for (set<int>::iterator setIt = it->second.begin(); setIt != it->second.end(); setIt++){
            board[i][j] = *setIt+'0';
            it++;
            if (isValidSudoku(board) && dfsCheck(board, candidates, it)){
                return true;
            }
            it--;
            board[i][j] = '.';
        }
    }
     
private:
    int get1D(int i, int j){
        return i * 9 + j;
    }
    int get2Di(int index){
        return index / 9;
    }
    int get2Dj(int index){
        return index % 9;
    }
 
    void prunning(vector<vector<char> > &board, int i, int j, map<int, set<int>> &candidates){
        pruningGrid(board, i, j, candidates);
        pruningRow(board, i, j, candidates);
        pruningColumn(board, i, j, candidates);
    }
 
    void pruningGrid(vector<vector<char> > &board, int i, int j, map<int, set<int>> &candidates){
        int index = get1D(i, j);
        while (i % 3 != 0){
            i--;
        }
        while (j % 3 != 0){
            j--;
        }
        for (int m = i; m < i + 3; m++){
            for (int n = j; n < j + 3; n++){
                if (board[m][n] != '.'){
                    candidates[index].erase(board[m][n] - '0');
                }
            }
        }
    }
 
    void pruningRow(vector<vector<char> > &board, int i, int j, map<int, set<int>> &candidates){
        int index = get1D(i, j);
        for (int m = 0; m < 9; m++){
            if (board[i][m] != '.'){
                candidates[index].erase(board[i][m] - '0');
            }
        }
    }
 
    void pruningColumn(vector<vector<char> > &board, int i, int j, map<int, set<int>> &candidates){
        int index = get1D(i, j);
        for (int m = 0; m < 9; m++){
            if (board[m][j] != '.'){
                candidates[index].erase(board[m][j] - '0');
            }
        }
    }
 
    bool isValidSudoku(vector<vector<char>>& board) {
        //验证每一行是否有效
        for (int i = 0; i<9; i++){
            if (!checkRowValid(board, i)){
                return false;
            }
        }
        //验证每一列是否有效
        for (int i = 0; i<9; i++){
            if (!checkColumnValid(board, i)){
                return false;
            }
        }
        //验证每一格是否有效
        for (int i = 0; i<9; i = i + 3){
            for (int j = 0; j<9; j = j + 3){
                if (!checkGridValid(board, i, j)){
                    return false;
                }
            }
        }
        return true;
    }
 
    //验证每个格是否有效,传入的是左上角的下标
    bool checkGridValid(vector<vector<char>>& board, int m, int n){
        bool flag[9];
        memset(flag, 0, sizeof(bool)* 9);
        for (int i = m; i<m + 3; i++){
            for (int j = n; j<n + 3; j++){
                if (board[i][j] == '.'){
                    continue;
                }
                if (flag[board[i][j] - '1']){
                    return false;
                }
                flag[board[i][j] - '1'] = true;
            }
        }
        return true;
    }
 
    //验证每一行是否有效,传入的是行号
    bool checkRowValid(vector<vector<char>>& board, int m){
        bool flag[9];
        memset(flag, 0, sizeof(bool)* 9);
        for (int i = 0; i<9; i++){
            if (board[m][i] == '.'){
                continue;
            }
            if (flag[board[m][i] - '1']){
                return false;
            }
            flag[board[m][i] - '1'] = true;
        }
        return true;
    }
 
    //验证每一列是否有效,传入的是列号
    bool checkColumnValid(vector<vector<char>>& board, int n){
        bool flag[9];
        memset(flag, 0, sizeof(bool)* 9);
        for (int i = 0; i<9; i++){
            if (board[i][n] == '.'){
                continue;
            }
            if (flag[board[i][n] - '1']){
                return false;
            }
            flag[board[i][n] - '1'] = true;
        }
        return true;
    }
};
只包含唯一余数法。113ms

class Solution {
public:
    void solveSudoku(vector<vector<char> > &board) {
        set<int> allCan({ 1, 2, 3, 4, 5, 6, 7, 8, 9 });
        map<int, set<int>> candidates;
         
        for (int i = 0; i<9; i++){
            for (int j = 0; j<9; j++){
                if (board[i][j] == '.'){
                    candidates[get1D(i, j)] = allCan;
                    prunning(board, i, j, candidates);
                }
            }
        }
        while (candidates.size()>0){
            int unknowNumber = candidates.size();
 
            //唯一余数法
            for (map<int, set<int>>::iterator it = candidates.begin(); it != candidates.end();){
                if (it->second.size() == 1){
                    int i = get2Di(it->first);
                    int j = get2Dj(it->first);
                    board[i][j] = *(it->second.begin()) + '0';
                    candidates.erase(it++);
                }
                else{
                    it++;
                }
            }
            for (int i = 0; i<9; i++){
                for (int j = 0; j<9; j++){
                    if (board[i][j] == '.'){
                        prunning(board, i, j, candidates);
                    }
                }
            }
 
            if (unknowNumber == candidates.size()){
                break;
            }
        }
 
        //若仍未解答出来,则暴力枚举
        if (candidates.size() > 0){
            dfsCheck(board, candidates, candidates.begin());
        }
    }
 
    bool dfsCheck(vector<vector<char> > &board, map<int, set<int>> &candidates, map<int, set<int>>::iterator it){
        if (it == candidates.end()){
            return true;
        }
        int i = get2Di(it->first);
        int j = get2Dj(it->first);
        for (set<int>::iterator setIt = it->second.begin(); setIt != it->second.end(); setIt++){
            board[i][j] = *setIt+'0';
            it++;
            if (isValidSudoku(board) && dfsCheck(board, candidates, it)){
                return true;
            }
            it--;
            board[i][j] = '.';
        }
    }
     
private:
    int get1D(int i, int j){
        return i * 9 + j;
    }
    int get2Di(int index){
        return index / 9;
    }
    int get2Dj(int index){
        return index % 9;
    }
     
    void prunning(vector<vector<char> > &board, int i, int j, map<int, set<int>> &candidates){
        pruningGrid(board, i, j, candidates);
        pruningRow(board, i, j, candidates);
        pruningColumn(board, i, j, candidates);
    }
 
    void pruningGrid(vector<vector<char> > &board, int i, int j, map<int, set<int>> &candidates){
        int index = get1D(i, j);
        while (i % 3 != 0){
            i--;
        }
        while (j % 3 != 0){
            j--;
        }
        for (int m = i; m < i + 3; m++){
            for (int n = j; n < j + 3; n++){
                if (board[m][n] != '.'){
                    candidates[index].erase(board[m][n] - '0');
                }
            }
        }
    }
 
    void pruningRow(vector<vector<char> > &board, int i, int j, map<int, set<int>> &candidates){
        int index = get1D(i, j);
        for (int m = 0; m < 9; m++){
            if (board[i][m] != '.'){
                candidates[index].erase(board[i][m] - '0');
            }
        }
    }
 
    void pruningColumn(vector<vector<char> > &board, int i, int j, map<int, set<int>> &candidates){
        int index = get1D(i, j);
        for (int m = 0; m < 9; m++){
            if (board[m][j] != '.'){
                candidates[index].erase(board[m][j] - '0');
            }
        }
    }
 
    bool isValidSudoku(vector<vector<char>>& board) {
        //验证每一行是否有效
        for (int i = 0; i<9; i++){
            if (!checkRowValid(board, i)){
                return false;
            }
        }
        //验证每一列是否有效
        for (int i = 0; i<9; i++){
            if (!checkColumnValid(board, i)){
                return false;
            }
        }
        //验证每一格是否有效
        for (int i = 0; i<9; i = i + 3){
            for (int j = 0; j<9; j = j + 3){
                if (!checkGridValid(board, i, j)){
                    return false;
                }
            }
        }
        return true;
    }
 
    //验证每个格是否有效,传入的是左上角的下标
    bool checkGridValid(vector<vector<char>>& board, int m, int n){
        bool flag[9];
        memset(flag, 0, sizeof(bool)* 9);
        for (int i = m; i<m + 3; i++){
            for (int j = n; j<n + 3; j++){
                if (board[i][j] == '.'){
                    continue;
                }
                if (flag[board[i][j] - '1']){
                    return false;
                }
                flag[board[i][j] - '1'] = true;
            }
        }
        return true;
    }
 
    //验证每一行是否有效,传入的是行号
    bool checkRowValid(vector<vector<char>>& board, int m){
        bool flag[9];
        memset(flag, 0, sizeof(bool)* 9);
        for (int i = 0; i<9; i++){
            if (board[m][i] == '.'){
                continue;
            }
            if (flag[board[m][i] - '1']){
                return false;
            }
            flag[board[m][i] - '1'] = true;
        }
        return true;
    }
 
    //验证每一列是否有效,传入的是列号
    bool checkColumnValid(vector<vector<char>>& board, int n){
        bool flag[9];
        memset(flag, 0, sizeof(bool)* 9);
        for (int i = 0; i<9; i++){
            if (board[i][n] == '.'){
                continue;
            }
            if (flag[board[i][n] - '1']){
                return false;
            }
            flag[board[i][n] - '1'] = true;
        }
        return true;
    }
};


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值