本文主要是介绍LeetCode题练习与总结:解数独,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
一、题目
编写一个程序,通过填充空格来解决数独问题。
数独的解法需 遵循如下规则:
- 数字
1-9
在每一行只能出现一次。 - 数字
1-9
在每一列只能出现一次。 - 数字
1-9
在每一个以粗实线分隔的3x3
宫内只能出现一次。(请参考示例图)
数独部分空格内已填入了数字,空白格用 '.'
表示。
二、解题思路
- 选择一个空格:在数独板上找一个还没有填数字的空格。
- 尝试填入数字:尝试在该空格中填入1到9之间的数字。
- 检查有效性:检查填入的数字是否满足数独的规则。这包括检查当前行、列和3x3的子宫格。
- 递归解决:如果填入的数字有效,递归地尝试解决剩余的空格。
- 回溯:如果在递归过程中发现当前填入的数字导致无法继续填入其他空格,回溯到上一个空格,尝试填入另一个数字。
三、具体代码
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)
,因为这里的常数可以忽略不计。
五、总结知识点
-
回溯算法:这是一种通过递归来实现的深度优先搜索策略。它尝试在给定的问题空间中搜索解决方案,并且在搜索过程中,如果遇到不可行的路径,会返回到上一步,尝试其他可能的选项。
-
递归:
solve
方法是一个递归函数,它尝试填入数字并递归地调用自身来解决剩余的空格。递归的基本情况是当所有空格都被填满时,这时会返回true
,表示找到了一个解决方案。 -
参数传递:
solve
方法通过board
、row
和col
三个参数来跟踪当前的数独板状态和下一个需要填充的空格位置。 -
边界检查:在
solve
方法的开始,有对row
和col
的边界检查,确保它们不会超出数独板的范围。 -
条件判断:代码中有多个条件判断,用于确定当前位置是否可以填入某个数字。这包括检查当前行、列和3x3的宫格内是否已经存在该数字。
-
有效性验证:
isValid
方法用于验证尝试填入的数字是否符合数独的规则。它检查了三个维度:行、列和3x3的宫格。 -
回溯:当尝试填入的数字导致冲突时,通过将当前位置的值重置为
'.'
(表示空格),算法会回溯到上一步,尝试其他可能的数字。 -
字符操作:代码中使用了字符型变量
num
来表示数字1到9,这是为了与数独板上的字符表示保持一致。 -
数学计算:在计算3x3宫格的起始行和列时,使用了取模运算
%
来确定当前位置所在的宫格。 -
空格处理:代码中对数独板上的空格(
'.'
)进行了特殊处理,只有当当前位置是空格时,才会尝试填入数字。
以上就是解决这个问题的详细步骤,希望能够为各位提供启发和帮助。
这篇关于LeetCode题练习与总结:解数独的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!