每天一道LeetCode-----数独盘求解

Valid Sudoku

原题链接Valid Sudoku
这里写图片描述
判断给定的数独盘是否有效,数独盘中可能有空位置。

简述一下数独的规则,参考连接Sudoku Puzzles - The Rules.

  1. 每一行,数字1-9只能出现一次
  2. 每一列,数字1-9只能出现一次
  3. 每个3 * 3方格中,数字1-9只能出现一次。这里3 * 3方格只包括9个,即图片中加粗黑线分开的9个方格

其实就是每个点只能存在1-9这九个数字中的一个,满足每一行,每一列,每个3 * 3方格不能出现重复的数字

判断一个数独盘是否有效,只需要判断是否满足上述三个规则即可。另外,如果给出的数独盘像图片那样有些地方没有填充数字,也没有关系。只需要判断有数字的部分就好,比如说第一行只有5, 3, 7,那么可以判断第一行没有出现重复的数字,是满足规则一的。


肯定需要全部遍历一遍,每遍历到一个位置,就判断它所在的行,所在的列,所在的3 * 3方格是否已经存在同样的数字了,如果存在,返回false,否则,将这个数字添加到行,列,3 * 3方格的记录中。
所以需要分别记录每一行,每一列,每一个3 * 3方格都有那些数字出现过,其实就是3个二维数组

  1. 记录每一行都出现了哪些数字,vector<vector<int>> rows(9, vector<int>(10, 0));
  2. 记录每一列都出现了哪些数字,vector<vector<int>> columns(9, vector<int>(10, 0));
  3. 记录每个3 * 3方格都出现了哪些数字,vector<vector<int>> boxes(9, vector<int>(10, 0));

对于3 * 3方格,这里用vector<vector<int>> boxes(9, vector<int>(10, 0));形式表示,意思是这个数独盘是由9个3 * 3方格,编号从0到8。对于某个位置(row, column)而言,它所在的3 * 3方格编号为row / 3 * 3 + column / 3

表示方法,假设当前位置为(i, j),数字为n

  1. rows[i][n]表示第i行出现数字n的个数,不是1就是0
  2. columns[j][n]表示第j列出现数字n的个数,不是1就是0
  3. boxes[i / 3 * 3 + j / 3][n]表示当前位置所在的方格出现数字n的个数,不是1就是0

代码如下

class Solution {
public:
    bool isValidSudoku(vector<vector<char>>& board) {
        vector<vector<int>> rows(9, vector<int>(10, 0));
        vector<vector<int>> columns(9, vector<int>(10, 0));
        vector<vector<int>> boxes(9, vector<int>(10, 0));

        for(int i = 0; i < board.size(); ++i)
        {
            for(int j = 0; j < board[i].size(); ++j)
            {
                if(board[i][j] == '.')
                    continue;

                int n = board[i][j] - '0';
                /* 如果之前有出现过(不为0),就说明数独盘无效 */
                if(rows[i][n] || columns[j][n] || boxes[i / 3 * 3 + j / 3][n])
                    return false;
                /* 否则,更新每一行,每一列,所在方格的内容 */
                else
                    rows[i][n] = columns[j][n] = boxes[i / 3 * 3 + j / 3][n] = 1;
            }
        }

        return true;
    }

};

扩展

Sudoku Solver

原题链接Sudoku Solver
这里写图片描述
给定一个有效的数独盘,解出结果。
解一个数独盘就是要求把所有的空格都填上数字,要求仍然是满足上述三个规则,即

  1. 每一行,数字1-9只能出现一次
  2. 每一列,数字1-9只能出现一次
  3. 每个3 * 3方格中,数字1-9只能出现一次。这里3 * 3方格只包括9个,即图片中加粗黑线分开的9个方格

对于某个空格,它所能填充的数字需要满足

  1. 在所在行没有出现过的
  2. 在所在行没有出现过的
  3. 在所在3 * 3方格没有出现过的

所以在上面的问题中,已经把每一行,每一列,每个3 * 3方格出现的数字都找出了,接下来就是深度优先(dfs)把每个空格填上数字即可,当然填充的数字需要满足上面的要求。
如果填充到某个位置发现没有可选的数字了,就说明之前的某个位置选择错了,就回退到之前的位置,选择另一个满足上述要求的数字

代码如下

class Solution {
public:
    void solveSudoku(vector<vector<char>>& board) {
        vector<vector<int>> rows(9, vector<int>(10, 0));
        vector<vector<int>> columns(9, vector<int>(10, 0));
        vector<vector<int>> boxes(9, vector<int>(10, 0));

        /* 计算每一行,每一列,每个3 * 3方格中每个数字是否出现 */
        for(int i = 0; i < board.size(); ++i)
        {
            for(int j = 0; j < board[i].size(); ++j)
            {
                if(board[i][j] == '.')
                    continue;

                int n = board[i][j] - '0';
                rows[i][n] = columns[j][n] = boxes[i / 3 * 3 + j / 3][n] = 1;
            }
        }

        bool done = false;
        dfs(board, rows, columns, boxes, 0, 0, done);   
    }

private:
    void dfs(vector<vector<char>>& board, vector<vector<int>>& rows,
             vector<vector<int>>& columns, vector<vector<int>>& boxes,
             int row, int column, bool& done)
    {
        /* 填充完成 */
        if(row >= board.size())
        {
            done = true;
            return;
        }
        /* 当前某一行的末尾,换到下一行 */
        else if(column >= board[row].size())
        {
            dfs(board, rows, columns, boxes, row + 1, 0, done);
        }
        /* 如果有数字,则不需要填充,继续下一个 */
        else if(board[row][column] != '.')
        {
            dfs(board, rows, columns, boxes, row, column + 1, done);
        }
        else
        {
            for(int n = 1; n <= 9; ++n)
            {
                /* 如果数字出现过,就不能填充到当前位置 */
                if(rows[row][n] || columns[column][n] || boxes[row / 3 * 3 + column / 3][n])
                    continue;

                /* 将填充的数字记录下来 */
                rows[row][n] = columns[column][n] = boxes[row / 3 * 3 + column / 3][n] = 1;
                board[row][column] = n + '0';
                /* 递归填充下一个空格 */
                dfs(board, rows, columns, boxes, row, column + 1, done);
                /* 如果完成,就退出 */
                if(done)
                {
                    return;
                }
                /* 否则,回到填充之前的状态,重新找数字 */
                else
                {
                    rows[row][n] = columns[column][n] = boxes[row / 3 * 3 + column / 3][n] = 0;
                    board[row][column] = '.';
                }
            }
        }
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值