LeetCode题练习与总结:解数独

2024-03-17 10:04
文章标签 leetcode 总结 练习 解数

本文主要是介绍LeetCode题练习与总结:解数独,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一、题目

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

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

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

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

二、解题思路

  1. 选择一个空格:在数独板上找一个还没有填数字的空格。
  2. 尝试填入数字:尝试在该空格中填入1到9之间的数字。
  3. 检查有效性:检查填入的数字是否满足数独的规则。这包括检查当前行、列和3x3的子宫格。
  4. 递归解决:如果填入的数字有效,递归地尝试解决剩余的空格。
  5. 回溯:如果在递归过程中发现当前填入的数字导致无法继续填入其他空格,回溯到上一个空格,尝试填入另一个数字。

三、具体代码

class Solution {public void solveSudoku(char[][] board) {if (board == null || board.length != 9 || board[0].length != 9) {return;}solve(board, 0, 0); // 从第一个空格开始递归解决}private boolean solve(char[][] board, int row, int col) {if (row == 9) { // 如果到达最后一行,意味着所有空格都已尝试,返回成功return true;}if (col == 9) { // 移动到下一行return solve(board, row + 1, 0);}if (board[row][col] != '.') { // 如果当前格有数字,移动到下一列return solve(board, row, col + 1);}// 尝试填入1到9之间的数字for (char num = '1'; num <= '9'; num++) {if (isValid(board, row, col, num)) {board[row][col] = num; // 填入数字if (solve(board, row, col + 1)) { // 递归解决下一个空格return true;}board[row][col] = '.'; // 回溯,移除数字}}return false; // 如果没有数字可以填入,返回失败}private boolean isValid(char[][] board, int row, int col, char num) {// 检查行for (int i = 0; i < 9; i++) {if (board[row][i] == num) {return false;}}// 检查列for (int i = 0; i < 9; i++) {if (board[i][col] == num) {return false;}}// 检查3x3子宫格int startRow = row - row % 3;int startCol = col - col % 3;for (int i = 0; i < 3; i++) {for (int j = 0; j < 3; j++) {if (board[startRow + i][startCol + j] == num) {return false;}}}return true; // 如果数字不违反规则,返回true}
}

四、时间复杂度和空间复杂度

1. 时间复杂度
  • 时间复杂度主要取决于尝试填充数字的次数以及每次尝试时的验证过程。
  • 尝试次数:最坏情况下,每个空格都尝试填入1到9之间的所有数字,因此尝试次数大约是 9^9(因为9x9的数独有81个空格,每个空格最多尝试9次)。
  • 验证过程:每次尝试填入数字时,都需要验证该数字是否有效,这包括检查当前行、列和3x3的宫。最坏情况下,每个验证过程需要检查9个元素,因此对于每次尝试,验证过程的时间复杂度是 O(9)
  • 综上所述,最坏情况下的时间复杂度是 O(9^9 * 9),即 O(9^10)
  • 这是一个非常巨大的数字,但在实际情况中,由于数独的规则限制,通常不需要尝试所有可能的数字组合。
2. 空间复杂度
  • 空间复杂度主要取决于递归调用栈的深度。
  • 递归调用栈:在最坏情况下,每次递归调用都可能需要进入一个新的递归层级,直到找到一个合适的数字或者填满所有空格。由于数独最多有81个空格,递归调用栈的最大深度为81。
  • 因此,空间复杂度是 O(递归调用栈深度),即 O(81)O(1),因为这里的常数可以忽略不计。

五、总结知识点

  1. 回溯算法:这是一种通过递归来实现的深度优先搜索策略。它尝试在给定的问题空间中搜索解决方案,并且在搜索过程中,如果遇到不可行的路径,会返回到上一步,尝试其他可能的选项。

  2. 递归solve 方法是一个递归函数,它尝试填入数字并递归地调用自身来解决剩余的空格。递归的基本情况是当所有空格都被填满时,这时会返回 true,表示找到了一个解决方案。

  3. 参数传递solve 方法通过 boardrowcol 三个参数来跟踪当前的数独板状态和下一个需要填充的空格位置。

  4. 边界检查:在 solve 方法的开始,有对 rowcol 的边界检查,确保它们不会超出数独板的范围。

  5. 条件判断:代码中有多个条件判断,用于确定当前位置是否可以填入某个数字。这包括检查当前行、列和3x3的宫格内是否已经存在该数字。

  6. 有效性验证isValid 方法用于验证尝试填入的数字是否符合数独的规则。它检查了三个维度:行、列和3x3的宫格。

  7. 回溯:当尝试填入的数字导致冲突时,通过将当前位置的值重置为 '.'(表示空格),算法会回溯到上一步,尝试其他可能的数字。

  8. 字符操作:代码中使用了字符型变量 num 来表示数字1到9,这是为了与数独板上的字符表示保持一致。

  9. 数学计算:在计算3x3宫格的起始行和列时,使用了取模运算 % 来确定当前位置所在的宫格。

  10. 空格处理:代码中对数独板上的空格('.')进行了特殊处理,只有当当前位置是空格时,才会尝试填入数字。

以上就是解决这个问题的详细步骤,希望能够为各位提供启发和帮助。

这篇关于LeetCode题练习与总结:解数独的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/818632

相关文章

Java通过驱动包(jar包)连接MySQL数据库的步骤总结及验证方式

《Java通过驱动包(jar包)连接MySQL数据库的步骤总结及验证方式》本文详细介绍如何使用Java通过JDBC连接MySQL数据库,包括下载驱动、配置Eclipse环境、检测数据库连接等关键步骤,... 目录一、下载驱动包二、放jar包三、检测数据库连接JavaJava 如何使用 JDBC 连接 mys

JavaSE正则表达式用法总结大全

《JavaSE正则表达式用法总结大全》正则表达式就是由一些特定的字符组成,代表的是一个规则,:本文主要介绍JavaSE正则表达式用法的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下... 目录常用的正则表达式匹配符正则表China编程达式常用的类Pattern类Matcher类PatternSynta

SQL中JOIN操作的条件使用总结与实践

《SQL中JOIN操作的条件使用总结与实践》在SQL查询中,JOIN操作是多表关联的核心工具,本文将从原理,场景和最佳实践三个方面总结JOIN条件的使用规则,希望可以帮助开发者精准控制查询逻辑... 目录一、ON与WHERE的本质区别二、场景化条件使用规则三、最佳实践建议1.优先使用ON条件2.WHERE用

Nginx Location映射规则总结归纳与最佳实践

《NginxLocation映射规则总结归纳与最佳实践》Nginx的location指令是配置请求路由的核心机制,其匹配规则直接影响请求的处理流程,下面给大家介绍NginxLocation映射规则... 目录一、Location匹配规则与优先级1. 匹配模式2. 优先级顺序3. 匹配示例二、Proxy_pa

Android学习总结之Java和kotlin区别超详细分析

《Android学习总结之Java和kotlin区别超详细分析》Java和Kotlin都是用于Android开发的编程语言,它们各自具有独特的特点和优势,:本文主要介绍Android学习总结之Ja... 目录一、空安全机制真题 1:Kotlin 如何解决 Java 的 NullPointerExceptio

MySQL基本查询示例总结

《MySQL基本查询示例总结》:本文主要介绍MySQL基本查询示例总结,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录Create插入替换Retrieve(读取)select(确定列)where条件(确定行)null查询order by语句li

Linux区分SSD和机械硬盘的方法总结

《Linux区分SSD和机械硬盘的方法总结》在Linux系统管理中,了解存储设备的类型和特性是至关重要的,不同的存储介质(如固态硬盘SSD和机械硬盘HDD)在性能、可靠性和适用场景上有着显著差异,本文... 目录一、lsblk 命令简介基本用法二、识别磁盘类型的关键参数:ROTA查询 ROTA 参数ROTA

Qt实现网络数据解析的方法总结

《Qt实现网络数据解析的方法总结》在Qt中解析网络数据通常涉及接收原始字节流,并将其转换为有意义的应用层数据,这篇文章为大家介绍了详细步骤和示例,感兴趣的小伙伴可以了解下... 目录1. 网络数据接收2. 缓冲区管理(处理粘包/拆包)3. 常见数据格式解析3.1 jsON解析3.2 XML解析3.3 自定义

Python实现图片分割的多种方法总结

《Python实现图片分割的多种方法总结》图片分割是图像处理中的一个重要任务,它的目标是将图像划分为多个区域或者对象,本文为大家整理了一些常用的分割方法,大家可以根据需求自行选择... 目录1. 基于传统图像处理的分割方法(1) 使用固定阈值分割图片(2) 自适应阈值分割(3) 使用图像边缘检测分割(4)

Windows Docker端口占用错误及解决方案总结

《WindowsDocker端口占用错误及解决方案总结》在Windows环境下使用Docker容器时,端口占用错误是开发和运维中常见且棘手的问题,本文将深入剖析该问题的成因,介绍如何通过查看端口分配... 目录引言Windows docker 端口占用错误及解决方案汇总端口冲突形成原因解析诊断当前端口情况解