LeetCode 37、解数独

99 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第20天,点击查看活动详情

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

数独的解法需要满足以下规则:

  1. 数字1-9在每行每列只能出现一次。
  2. 数字1-9在每个3*3的宫格内只能出现一次。

解题思路

解数独是一个常见的问题,本题并没有最终的结果集合,要求在原数组上修改,那么如果修改了原数组满足条件的话,那原数组对应的值就不能再回溯回去了,因此本题的回溯是有返回值的。

回溯法首先就要寻找返回值,但本题需要遍历数据中所有位置,对所有可填充位置进行填充,当遍历完成即回溯终止,因此不需要终止条件。

每次需要找到数组中可填充位置,之后遍历字符1-9,每次尝试填充,尝试填充需要判断其所在的行列的字符情况,并且需要判断宫格内的元素是否也满足条件,判断是否课填充代码如下:

public boolean isProperSo(char[][] board, int row, int column, char number) {
    // 检查列
        for (int i = 0; i < 9; i++) {
            if (board[row][i] == number) return false;
        }
        // 检查行
        for (int i = 0; i < 9; i++) {
            if (board[i][column] == number) return false;
        }
        // 检查正方形
        int startRow = (row / 3) * 3;
        int startCol = (column / 3) * 3;
        for (int i = startRow; i < startRow + 3; i++) {
            for (int j = startCol; j < startCol + 3; j++) {
                if (board[i][j] == number) {
                    return false;
                }
            }
        }
        return true;
}

在判断中,如果直接找到了满足条件的位置,此时可以直接终止,无需恢复字符,并且当试完全部元素还是没有返回true,则返回false,如果最终没有返回false,则代表已经找到了正确值,此时可直接返回true。完整代码如下:

public void solveSudoku(char[][] board) {
    backtrace(board);
}

public boolean backtrace(char[][] board) {
    for (int curRow = 0; curRow < 9; curRow++) {
        for (int i = 0; i < board[0].length; i++) {
            if (board[curRow][i] != '.') continue;
            for (char j = '1'; j <= '9'; j++) {
                if (isProperSo(board, curRow, i, j)) {
                    board[curRow][i] = j;
                    if(backtrace(board)){
                        return true;
                    }
                    board[curRow][i] = '.';
                }
            }
            return false;
        }
    }
    return true;
}