当前位置: 首页 > news >正文

回溯算法37:解数独

主要是我自己刷题的一些记录过程。如果有错可以指出哦,大家一起进步。
转载代码随想录
原文链接:
代码随想录
leetcode链接:37. 解数独

题目:

编写一个程序,通过填充空格来解决数独问题。

数独的解法需 遵循如下规则:

数字 1-9 在每一行只能出现一次。
数字 1-9 在每一列只能出现一次。
数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。(请参考示例图)

数独部分空格内已填入了数字,空白格用 ‘.’ 表示。

示例:

示例 1:
在这里插入图片描述

输入:board = [
[“5”,“3”,“.”,“.”,“7”,“.”,“.”,“.”,“.”],
[“6”,“.”,“.”,“1”,“9”,“5”,“.”,“.”,“.”],
[“.”,“9”,“8”,“.”,“.”,“.”,“.”,“6”,“.”],
[“8”,“.”,“.”,“.”,“6”,“.”,“.”,“.”,“3”],
[“4”,“.”,“.”,“8”,“.”,“3”,“.”,“.”,“1”],
[“7”,“.”,“.”,“.”,“2”,“.”,“.”,“.”,“6”],
[“.”,“6”,“.”,“.”,“.”,“.”,“2”,“8”,“.”],
[“.”,“.”,“.”,“4”,“1”,“9”,“.”,“.”,“5”],
[“.”,“.”,“.”,“.”,“8”,“.”,“.”,“7”,“9”]]

输出:[
[“5”,“3”,“4”,“6”,“7”,“8”,“9”,“1”,“2”],[“6”,“7”,“2”,“1”,“9”,“5”,“3”,“4”,“8”],[“1”,“9”,“8”,“3”,“4”,“2”,“5”,“6”,“7”],[“8”,“5”,“9”,“7”,“6”,“1”,“4”,“2”,“3”],[“4”,“2”,“6”,“8”,“5”,“3”,“7”,“9”,“1”],[“7”,“1”,“3”,“9”,“2”,“4”,“8”,“5”,“6”],[“9”,“6”,“1”,“5”,“3”,“7”,“2”,“8”,“4”],[“2”,“8”,“7”,“4”,“1”,“9”,“6”,“3”,“5”],[“3”,“4”,“5”,“2”,“8”,“6”,“1”,“7”,“9”]]
解释:输入的数独如上图所示,唯一有效的解决方案如下所示:
在这里插入图片描述

提示:

board.length == 9
board[i].length == 9
board[i][j] 是一位数字或者 '.'
题目数据 保证 输入数独仅有一个解

思路:

棋盘搜索问题可以使用回溯法暴力搜索,只不过这次我们要做的是二维递归

怎么做二维递归呢?

N皇后问题是因为每一行每一列只放一个皇后,只需要一层for循环遍历一行,递归来遍历列,然后一行一列确定皇后的唯一位置。

本题就不一样了,本题中棋盘的每一个位置都要放一个数字(而N皇后是一行只放一个皇后),并检查数字是否合法,解数独的树形结构要比N皇后更宽更深

因为这个树形结构太大了,我抽取一部分,如图所示:
在这里插入图片描述

回溯三部曲

递归函数以及参数

递归函数的返回值需要是bool类型,为什么呢

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

代码如下:

bool backtracking(vector<vector<char>>& board)

递归终止条件

本题递归不用终止条件,解数独是要遍历整个树形结构寻找可能的叶子节点就立刻返回。

不用终止条件会不会死循环

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

那么有没有永远填不满的情况呢

这个问题我在递归单层搜索逻辑里再来讲!

递归单层搜索逻辑

在这里插入图片描述在树形图中可以看出我们需要的是一个二维的递归(也就是两个for循环嵌套着递归)

一个for循环遍历棋盘的行,一个for循环遍历棋盘的列,一行一列确定下来之后,递归遍历这个位置放9个数字的可能性

代码如下:(详细看注释)

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;                // 放置kif (backtracking(board)) return true; // 如果找到合适一组立刻返回board[i][j] = '.';              // 回溯,撤销k}}return false;                           // 9个数都试完了,都不行,那么就返回false}}return true; // 遍历完没有返回false,说明找到了合适棋盘位置了
}

注意这里return false的地方,这里放return false 是有讲究的

因为如果一行一列确定下来了,这里尝试了9个数都不行,说明这个棋盘找不到解决数独问题的解!

那么会直接返回, 这也就是为什么没有终止条件也不会永远填不满棋盘而无限递归下去

判断棋盘是否合法

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

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

代码如下:

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;
}

最后整体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] == '.') {for (char k = '1'; k <= '9'; k++) {     // (i, j) 这个位置放k是否合适if (isValid(i, j, k, board)) {board[i][j] = k;                // 放置kif (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);}
};

总结

解数独可以说是非常难的题目了,如果还一直停留在单层递归的逻辑中,这道题目可以让大家瞬间崩溃。

所以我在开篇就提到了二维递归,这也是我自创词汇,希望可以帮助大家理解解数独的搜索过程。

一波分析之后,再看代码会发现其实也不难,唯一难点就是理解二维递归的思维逻辑。

自己的代码

class Solution {bool isUsed(int row, int col, char k, vector<vector<char>>& board) {for (int i = 0; i < 9; i++) {     //该列的后面(遍历这行)if (board[row][i] == k) return true;}for (int i = 0; i < 9; i++) {     //(遍历这列)if (board[i][col] == k) return true;}int startRowIndex = (row / 3) * 3;//行的起始下标int startColIndex = (col / 3) * 3;//列的起始下标for (int i = startRowIndex; i < startRowIndex + 3; i++) {for (int j = startColIndex; j < startColIndex + 3; j++) {if (board[i][j] == k)return true;}}return false;}bool dfs(vector<vector<char>>& board, int row) {/*vector<vector<char>> temp = board;*/if (row == 9)   return true;for (int col = 0; col < 9; col++) {           //遍历该行的这些列if (board[row][col]=='.') {           //这个列是空的需要填字。for (char k = '1'; k <= '9'; k++) {  //该空格尝试1-9.if (isUsed(row, col, k, board))  continue; //这个数字在遍历过的列中已经填过了或者不满足棋盘条件。board[row][col] =  k;    //(row,col)位置填数字k.bool result = dfs(board, row);if (result) return true;board[row][col] = '.';    //(row,i)位置做檫除}return false;}}return dfs(board, row + 1);return true;}
public:void solveSudoku(vector<vector<char>>& board) {dfs(board, 0);}
};
http://www.lryc.cn/news/44867.html

相关文章:

  • 【蓝桥杯-筑基篇】动态规划
  • Unity利用Photon PUN2框架快速实现多人在线游戏实例分享
  • ChatGPT直出1.5w字论文查重率才30% - 基于物联网技术的智能家居控制系统设计与实现
  • 特斯拉的操作系统是用什么语言编写的?
  • C++学习8-C++提高编程
  • ubuntu安装git server
  • 物流云数据分析平台
  • 配置OBS存储功能、新搭建obs
  • 基于DPDK收包的suricata的安装和运行
  • 浅谈23种设计模式
  • JetBrains Rider 2022.3.3 Crack
  • 浅理解扁平数据结构转Tree(树形结构)
  • 前端开发——JavaScript的条件语句
  • 2.11 循环赛日程表
  • SpringBoot——SB整合mybatis案例(残缺版本)第三集
  • Baumer工业相机堡盟相机不满帧如何使用CameraExplorer设置相机参数让它的帧率达到满帧
  • 巴黎爱情回忆 NFT 作品集
  • openai开放gpt3.5-turbo模型api,使用python即可写一个基于gpt的智能问答机器人
  • GUI开发--LCD屏幕的使用(非第三方库)--笔记
  • CesiumForUnreal实现地形等高线效果
  • Python爬虫——Python Selenium基本用法
  • 仿真与测试:单元测试与Test Harness
  • 面试常问集锦——MySQL部分
  • 算法训练第四十四天|完全背包理论 、518. 零钱兑换 II、377. 组合总和 Ⅳ
  • 0x06多层感知机
  • HTML是什么?HTML简介
  • Linux定时服务
  • sgi_stl源码学习,官方文档3.2.3String package字符串封装,未完待续
  • 从JavaScript到Java(一):基础知识
  • Android编舞者类Choreographer小结