题目要求

Write a program to solve a Sudoku puzzle by filling the empty cells.

Empty cells are indicated by the character '.'.

You may assume that there will be only one unique solution.

clipboard.png

clipboard.png

也就是给出一个解决数独的方法,假设每个数独只有一个解决方案。

思路一:不使用数据结构

不使用数据结构意味着我们自己在每一次遇到一个空格时,需要在board中对该空格所在的行,列以及方块进行遍历,判断填入什么数字是合适的。并将当前的假象结果带入下一轮的遍历之中。如果最终遍历失败,则逐级返回,恢复上一轮遍历的状态,并填入下一个合理的假想值后继续遍历。这里涉及的时间复杂度是很高的,毕竟只是遍历整个board就需要O(n^2),如果遇到一个空格还需要O(n)来判断填入值是否合理。总共O(n^3)的时间复杂度。代码如下:

    public void solveSudoku(char[][] board) {
        if(board == null || board.length == 0)
            return;
        solve(board);
    }
    
    public boolean solve(char[][] board){
        for(int i = 0; i < board.length; i++){
            for(int j = 0; j < board[0].length; j++){
                if(board[i][j] == '.'){
                    for(char c = '1'; c <= '9'; c++){//trial. Try 1 through 9
                        if(isValid(board, i, j, c)){
                            board[i][j] = c; //Put c for this cell
                            
                            if(solve(board))
                                return true; //If it's the solution return true
                            else
                                board[i][j] = '.'; //Otherwise go back
                        }
                    }
                    
                    return false;
                }
            }
        }
        return true;
    }
    
    //横 竖 方块
    private boolean isValid(char[][] board, int row, int col, char c){
        for(int i = 0; i < 9; i++) {
            if(board[i][col] != '.' && board[i][col] == c) return false; //check row
            if(board[row][i] != '.' && board[row][i] == c) return false; //check column
            if(board[3 * (row / 3) + i / 3][ 3 * (col / 3) + i % 3] != '.' && 
board[3 * (row / 3) + i / 3][3 * (col / 3) + i % 3] == c) return false; //check 3*3 block
        }
        return true;
    }

思路二:使用二维布尔数组分别记录行列和方块的所有值情况

我们使用boolean[][] rows, boolean[][] columns, boolean[][] squares分别记录行列和方块的所有值情况。其中rowsi记录i行是否有值j,columnsi记录i列是否有值j,squaresi记录第i个方块是否有值j。总体思路还是和上一个差不多,只是通过消费了存储空间的基础上提高了性能。

    public void solveSudoku2(char[][] board) {
        int length = board.length;
        //记录第i行是否出现j数字
        boolean[][] rows = new boolean[length][length+1];
        //第i列是否出现j数字
        boolean[][] columns = new boolean[length][length+1];
        //第i个小方格是否出现j数字。
        boolean[][] squares = new boolean[length][length+1];
        for(int i = 0 ; i < length ; i++){
            for(int j = 0 ; j<length ; j++){
                if(board[i][j] != '.'){
                    int value = board[i][j] - '0';
                    rows[i][value] = true;
                    columns[j][value] = true;
                    squares[i / 3 * 3 + j / 3][value] = true;
                }
            }
        }
        solveSudoku(board, 0, rows, columns, squares);
    }
    
    private boolean solveSudoku(char[][] board, int index, boolean[][] rows, boolean[][] columns, boolean[][] squares){
        if(index == 81){
            return true;
        }
        int row = index / 9;
        int column = index % 9;
        int square = row/3*3 + column/3;
        if(board[row][column]=='.'){
            for(char i = '1' ; i<='9' ; i++){
                if(rows[row][i-'0'] || columns[i-'0'][column] || squares[square][i-'0']) continue;
                
                board[row][column] = i;
                rows[row][i-'0'] = true;
                columns[column][i-'0'] = true;
                squares[square][i-'0'] = true;
                if(solveSudoku(board, index+1, rows, columns, squares)){
                    return true;
                }else{
                    board[row][column] = '.';
                    rows[row][i-'0'] = false;
                    columns[column][i-'0'] = false;
                    squares[square][i-'0'] = false;
                }
            }
            return false;
        }
        return solveSudoku(board, index+1, rows, columns, squares);
    }

更多解法

看到一个解答 效率很高 但是不是很明白 希望看到的大神解答一下

    private boolean solver(int idx, char[][] board, ArrayList<Integer> stack, int[] store) {
        if (idx == stack.size()) return true;
        int n = stack.get(idx);
        int y = n / 9;
        int x = n - y * 9;
        int h = y;
        int v = 9 + x;
        int b = 18 + (y / 3 * 3 + x / 3);
        int available = ~store[h] & ~store[v] & ~store[b] & 0b111111111;
        while (available > 0) {
            int bit = available & -available;
            int num = Integer.numberOfTrailingZeros(bit);
            store[h] ^= bit;
            store[v] ^= bit;
            store[b] ^= bit;
            board[y][x] = (char)(num + '1');
            if (solver(idx + 1, board, stack, store)) return true;
            store[h] ^= bit;
            store[v] ^= bit;
            store[b] ^= bit;
            // board[y][x] = '.';
            available &= available - 1;
        }
        return false;
    }
    public void solveSudoku3(char[][] board) {
        ArrayList<Integer> stack =  new ArrayList<>();
        // int[] stack = new int[81];
        int len = 0;
        int[] store = new int[27]; // 0-8 h, 9 - 17 v, 18 - 26 b
        for (int i = 0; i < 9; i++) {
            for (int j = 0; j < 9; j++) {
                if (board[i][j] == '.') stack.add(i * 9 + j);
                else {
                    int h = i;
                    int v = 9 + j;
                    int b = 18 + (i / 3 * 3 + j / 3);
                    store[h] ^= 1 << board[i][j] - '1';
                    store[v] ^= 1 << board[i][j] - '1';
                    store[b] ^= 1 << board[i][j] - '1';
                }
            }
        }
        solver(0, board, stack, store);
    }

clipboard.png
想要了解更多开发技术,面试教程以及互联网公司内推,欢迎关注我的微信公众号!将会不定期的发放福利哦~


raledong
2.7k 声望2k 粉丝

心怀远方,负重前行