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整个代码主干和它的整个判重的逻辑,可认为是回溯