C++实现LeetCode(37.求解数独)

 更新时间:2021年07月14日 15:47:53   作者:Grandyang  
这篇文章主要介绍了C++实现LeetCode(37.求解数独),本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下

[LeetCode] 37. Sudoku Solver 求解数独

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

A sudoku solution must satisfy all of the following rules:

  1. Each of the digits 1-9 must occur exactly once in each row.
  2. Each of the digits 1-9 must occur exactly once in each column.
  3. Each of the the digits 1-9 must occur exactly once in each of the 9 3x3 sub-boxes of the grid.

Empty cells are indicated by the character '.'.


A sudoku puzzle...


...and its solution numbers marked in red.

Note:

  • The given board contain only digits 1-9and the character '.'.
  • You may assume that the given Sudoku puzzle will have a single unique solution.
  • The given board size is always 9x9.

这道求解数独的题是在之前那道 Valid Sudoku 的基础上的延伸,之前那道题让我们验证给定的数组是否为数独数组,这道让求解数独数组,跟此题类似的有 PermutationsCombinations N-Queens 等等,其中尤其是跟 N-Queens 的解题思路及其相似,对于每个需要填数字的格子带入1到9,每代入一个数字都判定其是否合法,如果合法就继续下一次递归,结束时把数字设回 '.',判断新加入的数字是否合法时,只需要判定当前数字是否合法,不需要判定这个数组是否为数独数组,因为之前加进的数字都是合法的,这样可以使程序更加高效一些,整体思路是这样的,但是实现起来可以有不同的形式。一种实现形式是递归带上横纵坐标,由于是一行一行的填数字,且是从0行开始的,所以当i到达9的时候,说明所有的数字都成功的填入了,直接返回 ture。当j大于等于9时,当前行填完了,需要换到下一行继续填,则继续调用递归函数,横坐标带入 i+1。否则看若当前数字不为点,说明当前位置不需要填数字,则对右边的位置调用递归。若当前位置需要填数字,则应该尝试填入1到9内的所有数字,让c从1遍历到9,每当试着填入一个数字,都需要检验是否有冲突,使用另一个子函数 isValid 来检验是否合法,假如不合法,则跳过当前数字。若合法,则将当前位置赋值为这个数字,并对右边位置调用递归,若递归函数返回 true,则说明可以成功填充,直接返回 true。不行的话,需要重置状态,将当前位置恢复为点。若所有数字都尝试了,还是不行,则最终返回 false。检测当前数组是否合法的原理跟之前那道 Valid Sudoku 非常的相似,但更简单一些,因为这里只需要检测新加入的这个数字是否会跟其他位置引起冲突,分别检测新加入数字的行列和所在的小区间内是否有重复的数字即可,参见代码如下:

解法一:

class Solution {
public:
    void solveSudoku(vector<vector<char>>& board) {
        helper(board, 0, 0);
    }
    bool helper(vector<vector<char>>& board, int i, int j) {
        if (i == 9) return true;
        if (j >= 9) return helper(board, i + 1, 0);
        if (board[i][j] != '.') return helper(board, i, j + 1);
        for (char c = '1'; c <= '9'; ++c) {
            if (!isValid(board, i , j, c)) continue;
            board[i][j] = c;
            if (helper(board, i, j + 1)) return true;
            board[i][j] = '.';
        }
        return false;
    }
    bool isValid(vector<vector<char>>& board, int i, int j, char val) {
        for (int x = 0; x < 9; ++x) {
            if (board[x][j] == val) return false;
        }
        for (int y = 0; y < 9; ++y) {
            if (board[i][y] == val) return false;
        }
        int row = i - i % 3, col = j - j % 3;
        for (int x = 0; x < 3; ++x) {
            for (int y = 0; y < 3; ++y) {
                if (board[x + row][y + col] == val) return false;
            }
        }
        return true;
    }
};

还有另一种递归的写法,这里就不带横纵坐标参数进去,由于递归需要有 boolean 型的返回值,所以不能使用原函数。因为没有横纵坐标,所以每次遍历都需要从开头0的位置开始,这样无形中就有了大量的重复检测,导致这种解法虽然写法简洁一些,但击败率是没有上面的解法高的。这里的检测数组冲突的子函数写法也比上面简洁不少,只用了一个 for 循环,用来同时检测行列和小区间是否有冲突,注意正确的坐标转换即可,参见代码如下:

解法二:

class Solution {
public:
    void solveSudoku(vector<vector<char>>& board) {
        helper(board);
    }
    bool helper(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 c = '1'; c <= '9'; ++c) {
                    if (!isValid(board, i, j, c)) continue;
                    board[i][j] = c;
                    if (helper(board)) return true;
                    board[i][j] = '.';
                }
                return false;
            }
        }
        return true;
    }
    bool isValid(vector<vector<char>>& board, int i, int j, char val) {
        for (int k = 0; k < 9; ++k) {
            if (board[k][j] != '.' && board[k][j] == val) return false;
            if (board[i][k] != '.' && board[i][k] == val) return false;
            int row = i / 3 * 3 + k / 3, col = j / 3 * 3 + k % 3;
            if (board[row][col] != '.' && board[row][col] == val) return false;
        }
        return true;
    }
};

到此这篇关于C++实现LeetCode(37.求解数独)的文章就介绍到这了,更多相关C++实现求解数独内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • C++利用数组(一维/二维)处理批量数据的方法

    C++利用数组(一维/二维)处理批量数据的方法

    对于简单的问题,使用简单的数据类型就可以了,但是对于有些需要处理的数据,只用以上简单的数据类型是不够的,难以反映出数据的特点,也难以有效的进行处理,本文小编给大家介绍了C++利用数组(一维/二维)处理批量数据的方法,需要的朋友可以参考下
    2023-10-10
  • 如何用矩形法(梯形法)求定积分

    如何用矩形法(梯形法)求定积分

    思路就是将积分区间划分成n等份,然后将这n等份近似看成矩形(或梯形),然后对所有的矩形(或梯形)的面积进行求和
    2013-09-09
  • C语言实现洗牌与发牌游戏

    C语言实现洗牌与发牌游戏

    这篇文章主要为大家详细介绍了C语言洗牌与发牌游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2020-12-12
  • Qt5升级到Qt6分步迁移教程小结

    Qt5升级到Qt6分步迁移教程小结

    Qt框架的一个新的长期支持版本6.5最近发布,本文主要介绍了Qt5升级到Qt6分步迁移教程小结,具有一定的参考价值,感兴趣的可以了解一下
    2023-09-09
  • C++ Qt实现音视频播放功能

    C++ Qt实现音视频播放功能

    Qt版本 5.9 基于C++11 Qt核心组件与附加组件安装时请打钩 否则可能出现项目中缺少视频播放模块的问题,由于最近着手的Qt项目需要视频播放自己做的时候踩很多坑避免以后踩坑,故在此记录实现过程,感谢的朋友参考下吧
    2021-11-11
  • 10行C++代码实现高性能HTTP服务

    10行C++代码实现高性能HTTP服务

    这篇文章主要介绍了10行C++代码如何实现高性能HTTP服务,帮助大家更好的理解和学习使用c++,感兴趣的朋友可以了解下
    2021-04-04
  • C语言 二级指针详解及示例代码

    C语言 二级指针详解及示例代码

    本文主要介绍C语言 二级指针,这里整理了C语言中二级指针的基础资料并附有示例代码和实现结果,帮助大家学习理解相关知识,有学习的朋友可以参考下
    2016-08-08
  • C++中引用(&)的用法与应用实例分析

    C++中引用(&)的用法与应用实例分析

    引用是C++引入的新语言特性,是C++常用的一个重要内容之一,正确、灵活地使用引用,可以使程序简洁、高效。故在本篇中我将对引用进行详细讨论,希望对大家更好地理解和使用引用起到抛砖引玉的作用
    2013-09-09
  • C++ Eigen库实现最小二乘拟合的示例代码

    C++ Eigen库实现最小二乘拟合的示例代码

    Eigen 是一个线性算术的 C++ 模板库,功能强大、快速、优雅以及支持多平台,本文主要为大家介绍了C++利用Eigen库实现最小二乘拟合的示例代码,希望对大家有所帮助
    2023-07-07
  • C++连连看判定图形消除算法

    C++连连看判定图形消除算法

    这篇文章主要为大家详细介绍了C++连连看判定图形消除算法,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-12-12

最新评论