leetcode37 解数独

54 篇文章 2 订阅
10 篇文章 0 订阅

题目描述
编写一个程序,通过填充空格来解决数独问题。
数独的解法需 遵循如下规则:

  • 数字 1-9 在每一行只能出现一次。
  • 数字 1-9 在每一列只能出现一次。
  • 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。
    数独部分空格内已填入了数字,空白格用 ‘.’ 表示。

题解:回溯
遍历顺序都是从空格开始依次向后遍历。两种回溯模板:
第一种模板:

先将数独中的信息保存到三个bool数组中,一遍后续判断是否行列以及3*3宫格内是否出现过该数字,三个bool数组的含义。row[x][k]表示在第x行是否出现了数字k。cell[i][j][k]表示在第i行第j列的小单元格子里是否出现了数字k。

  • 如果遍历到了某一行最后一列,那么就要返回到下一行执行,也就是将行下标变为自加1,列下标变为0。如果遍历到了x==9,那么返回true。
  • 如果该位置是数字,那么继续向下遍历,行下标不变,列下标加一
  • 否则就需要判断当前位置是否应该填写该数字,就需要判断行列以及3*3宫格内是否写过该数字,如果没写过就在遍历到的位置写入该数字,之后撤销选择。
class Solution {
    bool row[9][9];
    bool col[9][9];
    bool cell[3][3][9];
public:
    void solveSudoku(vector<vector<char>> &board) {
        for (int i = 0; i < 9; i++) {
            for (int j = 0; j < 9; j++) {
                if (board[i][j] != '.') {
                    int t = board[i][j] - '1';
                    row[i][t] = col[j][t] = cell[i / 3][j / 3][t] = true;
                }
            }
        }
        dfs(board, 0, 0);
    }
    bool dfs(vector<vector<char>> &board, int x, int y) {
        if (y == 9) 
        {
            y=0;x++;
        }
        if (x == 9) return true;
        if (board[x][y] != '.') return dfs(board, x, y + 1);
        for (int i = 0; i < 9; i++) {
            if (!row[x][i] && !col[y][i] && !cell[x / 3][y / 3][i]) {
                board[x][y] = (char)(i + '1');
                row[x][i] = col[y][i] = cell[x / 3][y / 3][i] = true;
                //得到一组结果就返回
                if (dfs(board, x, y + 1)) {
                    break;
                } else {//撤销选择
                    board[x][y] = '.';
                    row[x][i] = col[y][i] = cell[x / 3][y / 3][i] = false;
                }
            }
        }
        return board[x][y] != '.';
    }
};

看出这两个版本的区别了吗?
一个在得到解答的时候break,此时函数还并未返回到调用函数,另一个是直接返回true,直接返回到调用函数位置,因此二者最后一行的返回语句是不一样的。return语句用于结束当前正在执行的函数,并将控制权返回给调用此函数的函数。具体执行过程我想了一下应该是这样的,对于上一个模板,break之后需要判断当前位置是否返回的是true,如果是就继续向递归树上走直到返回到最终原始调用的函数。对于下一个模板,直接返回到调用该函数的地方,不需要做判断。return false表示的含义是如果这时候都没有返回true,那么条件不满足返回false。因此两个模板后者比前者执行时间要快一些。

class Solution {
    bool row[9][9];
    bool col[9][9];
    bool cell[3][3][9];
public:
    void solveSudoku(vector<vector<char>> &board) {
        for (int i = 0; i < 9; i++) {
            for (int j = 0; j < 9; j++) {
                if (board[i][j] != '.') {
                    int t = board[i][j] - '1';
                    row[i][t] = col[j][t] = cell[i / 3][j / 3][t] = true;
                }
            }
        }
        dfs(board, 0, 0);
    }
    bool dfs(vector<vector<char>> &board, int x, int y) {
        if (y == 9) 
        {
            y=0;x++;
        }
        if (x == 9) return true;
        if (board[x][y] != '.') return dfs(board, x, y + 1);
        for (int i = 0; i < 9; i++) {
            if (!row[x][i] && !col[y][i] && !cell[x / 3][y / 3][i]) {
                board[x][y] = (char)(i + '1');
                row[x][i] = col[y][i] = cell[x / 3][y / 3][i] = true;
                if (dfs(board, x, y + 1)) {
                    return true;
                } else {
                    board[x][y] = '.';
                    row[x][i] = col[y][i] = cell[x / 3][y / 3][i] = false;
                }
            }
        }
        return false;
    }
};


第二种模板:
这种模板是在遍历到该位置时再判断是否可以填入该数字。注意返回false的位置与含义。

class Solution {
public:
    void solveSudoku(vector<vector<char> > &board) {
        backtracing(board);
    }
    bool backtracing(vector<vector<char>>& board)
    {
        for(int i=0;i<9;i++)
        {
            for(int j=0;j<9;j++)
            {
                if(board[i][j]!='.') continue;
                for(char k='1';k<='9';k++)
                {
                    if(isvalid(i,j,k,board))
                    {
                        board[i][j]=k;//该位置填写k
                        if(backtracing(board)) return true;
                        board[i][j]='.';//撤销选择
                    }  
                }
                return false;//九个数字都没办法填写返回false
            }
        }
        return true;//所有情况没返回false,则返回true
        
    }
    bool isvalid(int a,int b, char k,vector<vector<char>>& board)
    {
        int n=board.size();
        for(int i=0;i<n;i++) 
        {
            if(board[a][i]==k) return false;
        }
        for(int j=0;j<n;j++) 
        {
            if(board[j][b]==k) return false;
        }
        int col = (a/3)*3;
        int row = (b/3)*3;
        for(int i=col;i<col+3;i++)
        {
            for(int j=row;j<row+3;j++)
            {
                if(board[i][j]==k) return false;
            }
        }
        return true;
    }
};
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值