题解 | 数独

数独

http://www.nowcoder.com/practice/5e6c424b82224b85b64f28fd85761280

解法一:递归回溯

递归参数:

  • 因为解数独找到一个符合的条件(就在树的叶子节点上)立刻就返回,相当于找从根节点到叶子节点一条唯一路径,所以需要使用bool返回值,

终止条件:

  • 递归的下一层的棋盘一定比上一层的棋盘多一个数,等数填满了棋盘自然就终止(填满当然好了,说明找到结果了),所以不需要终止条件!

判断棋盘是否合法有如下三个维度:

  • 同行是否重复
  • 同列是否重复
  • 9宫格里是否重复

图片说明

图片;来自Carl大佬的[D码随想录]

C++参考代码:

class Solution {
private:
bool backtracking(vector<vector<char>>& board) {
    for (int i = 0; i < board.size(); i++) {        // 遍历行
        for (int j = 0; j < board[0].size(); j++) { // 遍历列
            if (board[i][j] != '.') continue;
            for (char k = '1'; k <= '9'; k++) {     // (i, j) 这个位置放k是否合适
                if (isValid(i, j, k, board)) {
                    board[i][j] = k;                // 放置k
                    if (backtracking(board)) return true; // 如果找到合适一组立刻返回
                    board[i][j] = '.';              // 回溯,撤销k
                }
            }
            return false;                           // 9个数都试完了,都不行,那么就返回false
        }
    }
    return true; // 遍历完没有返回false,说明找到了合适棋盘位置了
}
bool isValid(int row, int col, char val, vector<vector<char>>& board) {
    for (int i = 0; i < 9; i++) { // 判断行里是否重复
        if (board[row][i] == val) {
            return false;
        }
    }
    for (int j = 0; j < 9; j++) { // 判断列里是否重复
        if (board[j][col] == val) {
            return false;
        }
    }
    int startRow = (row / 3) * 3;
    int startCol = (col / 3) * 3;
    for (int i = startRow; i < startRow + 3; i++) { // 判断9方格里是否重复
        for (int j = startCol; j < startCol + 3; j++) {
            if (board[i][j] == val ) {
                return false;
            }
        }
    }
    return true;
}
public:
    void solveSudoku(vector<vector<char>>& board) {
        backtracking(board);
    }
};

复杂度分析:

时间复杂度:
由于这些方法均以递归 + 回溯为基础,算法运行的时间(以及时间复杂度)很大程度取决于给定的输入数据,而我们很难找到一个非常精确的渐进紧界。因此这里只给出一个较为宽松的渐进复杂度上界 图片说明 ,即最多有9×9 个空白格,每个格子可以填[1,9] 中的任意整数。

复杂度参考:LeetCode-Solution

空间复杂度:复杂度为 O(1) 在固定 9*9 的棋盘里,复杂度不随数据变化而变化。

解法二:位运算

经常玩数独的可能知道,在填数字的时候一般都是选择那些可填数字比少的位置来填,所以这里也可以使用这种方式。这里为了便于计算,稍微修改了一点,每行每列每个单元格都使用一个int数字来记录,因为int类型是32位,而数独的行和列以及单元格大小都是9,所以使用int来存储足够了,这里只使用int类型的低9位来存储。举个简单的例子,比如第一行已经填了3个数字,比如示例中的5,3,7。那么我们就可以使用一个int类型的数字来表示
图片说明

该解法参考作者:sdwwld

Java参考代码:

public class Solution {
               //数独的长宽大小
        final int N = 9;
        //行
        private int[] rows = new int[N];
        //列
        private int[] cols = new int[N];
        //单元格
        private int[][] cells = new int[3][3];

        public void solveSudoku(char[][] board) {
            //统计未填的个数
            int count = 0;
       

剩余60%内容,订阅专栏后可继续查看/也可单篇购买

小白专属-牛客题解 文章被收录于专栏

专注于牛客平台编程题题解,文字+图解。内容很细,小白友好系列

全部评论

相关推荐

06-13 17:33
门头沟学院 Java
顺序不记了,大致顺序是这样的,有的相同知识点写分开了1.基本数据类型2.基本数据类型和包装类型的区别3.==和equals区别4.ArrayList与LinkedList区别5.hashmap底层原理,put操作时会发生什么6.说出几种树型数据结构7.B树和B+树区别8.jvm加载类机制9.线程池核心参数10.创建线程池的几种方式11.callable与runnable区别12.线程池怎么回收线程13.redis三剑客14.布隆过滤器原理,不要背八股,说说真正使用时遇到了问题没有(我说没有,不知道该怎么回答了)15.堆的内存结构16.自己在写项目时有没有遇见过oom,如何处理,不要背八股,根据真实经验,我说不会17.redis死锁怎么办,watchdog机制如何发现是否锁过期18.如何避免redis红锁19.一个表性别与年龄如何加索引20.自己的项目的QPS怎么测的,有没有真正遇到大数量表21.说一说泛型22.springboot自动装配原理23.springmvc与springboot区别24.aop使用过嘛?动态代理与静态代理区别25.spring循环依赖怎么解决26.你说用过es,es如何分片,怎么存的数据,1000万条数据怎么写入库中27.你说用limit,那么在数据量大之后,如何优化28.rabbitmq如何批次发送,批量读取,答了延迟队列和线程池,都不对29.计网知不知道smtp协议,不知道写了对不对,完全听懵了30.springcloud知道嘛?只是了解反问1.做什么的?短信服务,信息量能到千万级2.对我的建议,基础不错,但是不要只背八股,多去实际开发中理解。面试官人不错,虽然没露脸,但是中间会引导我回答问题,不会的也只是说对我要求没那么高。面完问我在济宁生活有没有困难,最快什么时候到,让人事给我聊薪资了。下午人事打电话,问我27届的会不会跑路,还在想办法如何使我不跑路,不想扣我薪资等。之后我再联系吧,还挺想去的😭,我真不跑路哥😢附一张河科大幽默大专图,科大就是大专罢了
查看30道真题和解析
点赞 评论 收藏
分享
评论
1
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务