剪枝 力扣 《解数独》 Java版本

37.解数独
编写一个程序,通过已填充的空格来解决数独问题。
一个数独的解法需遵循如下规则:
数字 1-9 在每一行只能出现一次。
数字 1-9 在每一列只能出现一次。
数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。
空白格用 ‘.’ 表示。
在这里插入图片描述
一个数独。
在这里插入图片描述

答案被标成红色。

Note:
给定的数独序列只包含数字 1-9 和字符 ‘.’ 。
你可以假设给定的数独只有唯一解。
给定数独永远是 9x9 形式的。

解决方案一:(Java版本 8ms 39.2MB)

public class Solution {
    public void solveSudoku(char[][] board) {//入口
        dfs(board,0);
    }
    private boolean dfs(char[][] board, int d) {
        if (d==81) return true; //found solution,递归终止条件
        int i=d/9, j=d%9;//代表横纵坐标
        if (board[i][j]!='.') return dfs(board,d+1);//prefill number skip,格子不是点,证明已有数字,,当前层不用考虑了,直接下一层就行了  不用恢复递归的状态了
        boolean[] flag=new boolean[10];
        validate(board,i,j,flag);//如果通过了是点的情况
        for (int k=1; k<=9; k++) {
            if (flag[k]) {
                board[i][j]=(char)('0'+k);//在board i,j里面填上1到9所有的值
                if (dfs(board,d+1)) return true;//填进去后继续走dfs,同样也是把d+1放进去,就相当于下传到新的一层去,所以为d+1
            }
        }
        board[i][j]='.'; //if can not solve, in the wrong path, change back to '.' and out  我们要把borad i,j 恢复到空的,这样才可继续回溯起来
        return false;
    }
    private void validate(char[][] board, int i, int j, boolean[] flag) {//判断是否有重复数字
        Arrays.fill(flag,true);
        for (int k=0; k<9; k++) {
            if (board[i][k]!='.') flag[board[i][k]-'0']=false;
            if (board[k][j]!='.') flag[board[k][j]-'0']=false;
            int r=i/3*3+k/3;
            int c=j/3*3+k%3;
            if (board[r][c]!='.') flag[board[r][c]-'0']=false;
        }
    }
}

解决方案二:(Java版本 22ms 36.7MB)

public class Solution {
    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,把1--9尝试往里边填
                        if(isValid(board, i, j, c)){//每次填了后判断整个棋盘是否合法
                            board[i][j] = c; //Put c for this cell,如果合法就把格子上试着填上c,就填进去了
                            
                            if(solve(board))//再递归调用solve
                                return true; //If it's the solution return true,如果solve能解决了,return true
                            else//else一词可省略,下边board直接提上去
                                board[i][j] = '.'; //Otherwise go back,然后再恢复上一级的状态
                        }
                    }   
                    return false;//如果什么都不行,返回false
                }
            }
        }
        return true;//如果整个棋盘也可以搞完的话,最后也是return true
    }
    //没有用所谓的set数组直接存起来,所以它每次都要这么循环,这就为手动硬进行判重计算,这样效率稍微是有点重复,计算量在里面的效率并不高
    private boolean isValid(char[][] board, int row, int col, char c){//棋盘如何判断合法非法
        for(int i = 0; i < 9; i++) {//写一个循环,看整个它列、行还有block
            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 ,检查整个block里边是否没有重复的元素,即没有重复的c
        }
        return true;//没有任何一个地方碰到重复元素,即合法
    }
}//文中DFS整个代码主干和它的整个判重的逻辑,可认为是回溯

猜你喜欢

转载自blog.csdn.net/gcyqweasd/article/details/106040378
今日推荐