37. Sudoku Solver

这道题和36. Valid Sudoku是配套的,如果理解了那道题目中的数独生成程序,这道题就很简单,程序稍微改改就可以了,核心思路是一样的。

而且这道题限定了给定的数独只有唯一一个正确解,又简化很多。

题外话:据说如果要确定只有唯一正确解,至少要给出17个元素。

我的解法:

public class Solution {
    // 其实leetcode中最好不要用这种成员变量或者static变量,可能引发些奇怪的问题
    // 见https://leetcode.com/faq/#different-output
    // 但是我需要一个全局变量,表示当前是否找到了一个解
    private boolean success = false;

    public void solveSudoku(char[][] board) {
        int[][] rowFlag = new int[10][10];
        int[][] colFlag = new int[10][10];
        int[][] cubeFlag = new int[10][10];
        // 遍历给定的数独,初始化状态
        for (int row = 0; row < 9; row++) {
            for (int col = 0; col < 9; col++) {
                if (board[row][col] == '.')
                    continue;
                int cubeNum = (row / 3) * 3 + col / 3;
                int value = board[row][col] - '0';
                rowFlag[row][value] = 1;
                colFlag[col][value] = 1;
                cubeFlag[cubeNum][value] = 1;
            }
        }
        // 每次计算前都要初始化success变量
        success = false;
        // 找出[0,0]之后需要填的下一个元素
        Point firstCell = findNextCell(board, 0, 0);
        // 如果[0,0]是需要填的,就从[0,0]开始
        if (board[0][0] == '.')
            fillBoard(board, 0, 0, rowFlag, colFlag, cubeFlag);
        // 否则从下一个格子开始
        else if (firstCell != null)
            fillBoard(board, firstCell.x, firstCell.y, rowFlag, colFlag, cubeFlag);
    }

    public void fillBoard(char[][] board, int row, int col, int[][] rowFlag, int[][] colFlag, int[][] cubeFlag) {
        int cubeNum = (row / 3) * 3 + col / 3;
        Point nextCell = findNextCell(board, row, col);
        // 这个格子可能填写1~9,一个个试过去
        for (int i = 1; i <= 9; i++) {
            if (rowFlag[row][i] == 0 && colFlag[col][i] == 0 && cubeFlag[cubeNum][i] == 0) {
                board[row][col] = (char) ('0' + i);
                rowFlag[row][i] = 1;
                colFlag[col][i] = 1;
                cubeFlag[cubeNum][i] = 1;
                // 如果没有下一个格子要填了,说明我们已经找到了正确的解,将success变量置为true
                // 否则就继续填写下一个格子
                if (nextCell == null) {
                    success = true;
                } else
                    fillBoard(board, nextCell.x, nextCell.y, rowFlag, colFlag, cubeFlag);

                // ATTENTION!
                // 注意这里,sucess是个全局变量,如果已经找到了正确的解,所有嵌套的递归都要直接返回,不用再继续尝试了
                if (success)
                    return;

                // 状态rollback,注意同时要把borad的状态还原,不然findNextCell时会出错
                board[row][col] = '.'; // ATTENTION!
                rowFlag[row][i] = 0;
                colFlag[col][i] = 0;
                cubeFlag[cubeNum][i] = 0;
            }
        }
    }

    // 找到下一个需要填写的格子,不包括当前格
    public Point findNextCell(char[][] board, int row, int col) {
        int i = row, j = col;
        while (i < 9 && j < 9) {
            j++;
            if (j >= 9) {
                i++;
                j = 0;
            }
            if (i < 9) {
                if (board[i][j] == '.') {
                    //System.out.println("next cell for (" + row + "," + col + ") is (" + i + "," + j + ")");
                    return new Point(i, j);
                }
            }
        }
        return null;
    }
}
// 一个辅助类
class Point {
    int x;
    int y;

    Point() {
        x = 0;
        y = 0;
    }

    Point(int a, int b) {
        x = a;
        y = b;
    }
}

比较容易出错的地方有2个:1.对success变量的处理;2.还原board的状态。

很意外的是这个解法居然能beat 94.3% java submissions,我目前的最好成绩了吧。。。

results matching ""

    No results matching ""