LEETCODE算法注解37:
编写一个程序,通过已填充的空格来解决数独问题。
一个数独的解法需遵循如下规则:
数字 1-9 在每一行只能出现一次。
数字 1-9 在每一列只能出现一次。
数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。
空白格用 '.' 表示。
Note:
给定的数独序列只包含数字 1-9 和字符 '.' 。
你可以假设给定的数独只有唯一解。
给定数独永远是 9x9 形式的。
此处不给图片了。
此处讲一下个人做这个题的感觉,其实说思路也不算难:假如给A空格填入合适的值,接下来去填B空格,发现没有合适的值可填,要返回A修改值,再去填B的值。
但是这个代码好难写啊,真令人头大,几天没写出来,看了官方答案,写的真好啊。
回溯法是解决此题的关键,也就是我上面说的思路。但是大多数同学不了解具体过程,
此处以题目给出的例子来详细解释一下。(函数名字参考下面代码)
代码逻辑:
现在准备好写回溯函数了
backtrack(row = 0, col = 0)
从最左上角的方格开始 row = 0, col = 0。直到到达一个空方格。
从1 到 9 迭代循环数组,尝试放置数字 d 进入 (row, col) 的格子。
如果数字 d 还没有出现在当前行,列和子方块中:
将 d 放入 (row, col) 格子中。
记录下 d 已经出现在当前行,列和子方块中。
如果这是最后一个格子row == 8, col == 8 :
意味着已经找出了数独的解。
否则
放置接下来的数字。
如果数独的解还没找到(即没有合适的值填入空格):
将最后的数从 (row, col) 移除。
代码(附有详细注释)
class Solution {
int n = 3;
int N = n * n;
//因为可填充的最大值时9,所以为N+1=10.
int [][] rows = new int[N][N + 1];
int [][] columns = new int[N][N + 1];
int [][] boxes = new int[N][N + 1];
char[][] board;
boolean sudokuSolved = false;//用来判定是否结束的标记,初始为false,表明没结束,为true结束
//判定能否填入的方法
public boolean couldPlace(int d, int row, int col)
{
int idx = (row / n ) * n + col / n;//小九宫格
//d是填入的数字,row是行,含义:row行d数字 此坐标为不为0,代表已经填入 为0代表未曾填入 ,当三者和为0时代表此值可以填入
return rows[row][d] + columns[col][d] + boxes[idx][d] == 0;
}
//填入的方法
public void placeNumber(int d, int row, int col)
{
int idx = (row / n ) * n + col / n;//小九宫格
//初始化时三个数组值全为0, ++的含义是此行.此列.此九宫格已经有d这个数字了
rows[row][d]++;
columns[col][d]++;
boxes[idx][d]++;
board[row][col] = (char)(d + '0');//将数字填入board中
}
//填充失败,重新归‘.’
public void removeNumber(int d, int row, int col)
{
int idx = (row / n ) * n + col / n;
rows[row][d]--;
columns[col][d]--;
boxes[idx][d]--;
board[row][col] = '.';
}
public void placeNextNumbers(int row, int col)
{
//成功填充到最后一位
if ((col == N - 1) && (row == N - 1)) {
sudokuSolved = true;
}
//继续填充下一位
else {
if (col == N - 1) backtrack(row + 1, 0);
else backtrack(row, col + 1);
}
}
public void backtrack(int row, int col)
{
//此处未曾填充,填充范围1~9
if (board[row][col] == '.')
{
for (int d = 1; d < 10; d++)
{
if (couldPlace(d, row, col)) //判定此值填入,行 列 小九宫都没有重复
{
placeNumber(d, row, col);//填充当前值
placeNextNumbers(row, col);//填充下一个值
if (!sudokuSolved) removeNumber(d, row, col);//不满足要求,移除数字
}
}
}
else placeNextNumbers(row, col);//此处已被填充,填充下一个值
}
public void solveSudoku(char[][] board)
{
this.board = board;
//先双重循环将所有数值整理出来,填入行.列.小九宫格中。
for (int i = 0; i < N; i++)
{
for (int j = 0; j < N; j++)
{
char num = board[i][j];
if (num != '.') {
int d = Character.getNumericValue(num);
placeNumber(d, i, j);//填入的方法
}
}
}
backtrack(0, 0);//从第一个空格开始回溯
}
}
来源:CSDN
作者:qq_39502383
链接:https://blog.csdn.net/qq_39502383/article/details/104145128