力扣第三十七题-解数独

260 阅读3分钟

这是我参与8月更文挑战的第4天,活动详情查看:8月更文挑战

前言

力扣第三十七题 解数独 如下所示:

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

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

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

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

示例

image.png

一、思路

解数独就是通过不断在空格中填数字,直到满足调节的结果出现。 这个过程就是不断的往空格中填入数字,直到填完所有的数字且能满足三个条件,就是找到了解。

填写的步骤大致如下所示:

  1. 找到所有的空格
  2. 使用递归依次向空格中填入值,如果不能满足则向上回溯
  3. 直到出现结果为止

举个例子

此处以示例中的数独作为例子,如下所示:

image.png

因为是横向扫描的,所以 0, 2 是第一个空格的位置,我们从 1~9 依次向其中填入。如果满足条件就继续填下一个位置。

刚好第一个 0, 2 空格可以满足条件,故填上 1

image.png

继续往下填写后,会有下面这个结果。你会发现 0, 8 这个位置此时没有满足的值了。(不能填6,因为同一列中已经有一个6了)

image.png

所以此时向上回溯0, 7 这个位置,你会发现 0, 7 这个位置也是填不了 6 的,因为列中也有一个 6 了。

image.png

所以会继续向上回溯

......,经过不停的探索,最终会得到下面这个结果:

image.png

举个例子

二、实现

实现代码

实现代码与思路中保持一致,命名等可见注释

private boolean[][] rowRecords = new boolean[9][9];   // 行
private boolean[][] colRecords = new boolean[9][9]; // 列
private boolean[][] subBoxRecords = new boolean[9][9]; // 子盒子记录
private boolean valid = false;  // 满足条件
private List<int[]> spaces = new ArrayList<>(); // 空格的位置

public void solveSudoku(char[][] board) {
    for (int i = 0; i < 9; ++i) {
        for (int j = 0; j < 9; ++j) {
            if (board[i][j] == '.') {
                spaces.add(new int[]{i, j});
            } else {
                int digit = board[i][j] - '0' - 1;
                // 子盒子下标
                int boxIndex = i/3 + j/3*3;
                rowRecords[i][digit] = colRecords[j][digit] = subBoxRecords[boxIndex][digit] = true;
            }
        }
    }

    dfs(board, 0);
}

/**
 * 递归
 */
public void dfs(char[][] board, int pos) {
    // 满足条件直接返回
    if (pos == spaces.size()) {
        valid = true;
        return;
    }

    int[] space = spaces.get(pos);
    int i = space[0], j = space[1];
    for (int digit = 0; digit < 9 && !valid; ++digit) {
        // 子盒子下标
        int boxIndex = i/3 + j/3*3;
        // 确保行、列及子盒子都可以填
        if (!rowRecords[i][digit] && !colRecords[j][digit] && !subBoxRecords[boxIndex][digit]) {
            rowRecords[i][digit] = colRecords[j][digit] = subBoxRecords[boxIndex][digit] = true;
            board[i][j] = (char) (digit + '0' + 1);
            dfs(board, pos + 1);
            rowRecords[i][digit] = colRecords[j][digit] = subBoxRecords[boxIndex][digit] = false;
        }
    }
}

测试代码

public static void main(String[] args) {
    char[][] 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'}};
    new Number37().solveSudoku(board);
}

结果

image.png

三、总结

感谢看到最后,非常荣幸能够帮助到你~♥