题目描述
题目分析
建议食用本题前先看看36.有效的数独,解数独也使用到该题里面如何判断重复的思路。
- 本题用到的是回溯的思路,所谓回溯,其实就是在进行一步操作之后,违反了规则,而进行情景恢复的步骤,换句话说,回溯就是把你刚做的操作撤销,回到操作之前的样子,类似于Ctrl+Z。我们通过回溯的方法,对每一个格子填入1-9进行尝试,如果成功就进行下一步,失败即进行回溯,回到上一步。
- 要填入一个数,需要判断行、列和九宫格内是否有重复数字出现。我们可以使用三个二维数组来实现,例如rows[3][7]的值为1,就说明第三行中,7这个数已经出现了一次,如果要插入的时候,发现已经为1,则破坏了规则。
题目解答
class Solution {
//box大小
int n=3;
//数独大小
int N=n*n;
//保存行、列和3*3方格中的数
int[][] rows=new int[N][N+1];
int[][] columns=new int[N][N+1];
int[][] boxes=new int[N][N+1];
char[][] board;
//标记该行是否已完成
boolean sudokuSolved=false;
public void solveSudoku(char[][] board) {
this.board=board;
//初始化rows,columns和boxes
for(int i=0;i<N;i++){
for(int j=0;j<N;j++){
int num=board[i][j];
if(num!='.'){
int d=Character.getNumericValue(num);
placeNumber(d,i,j);
}
}
}
//开始方法
backtrack(0,0);
}
/**
*把数字放进rows、columns和boxes里面
*/
public void placeNumber(int d,int row,int col){
int idx=(row/n)*n+col/n;
//某行和某列的d位置+1,当d位置为2时,则重复
rows[row][d]++;
columns[col][d]++;
boxes[idx][d]++;
board[row][col]=(char)(d+'0');
}
//回溯法
public void backtrack(int row,int col){
if(board[row][col]=='.'){
for(int d=1;d<10;d++){
if(couldPlace(d,row,col)){
placeNumber(d,row,col);
placeNextNumber(row,col);
//如果还没有找到解,就进行回溯
if(!sudokuSolved) removeNumber(d,row,col);
}
}
}
else placeNextNumber(row,col);
}
//判断是否能放进格子
public boolean couldPlace(int d,int row,int col){
int idx=(row/n)*n+col/n;
return rows[row][d]+columns[col][d]+boxes[idx][d]==0;
}
//放入下个数字
public void placeNextNumber(int row,int col){
//如果到达边界的时候,说明已经找到解
if((col==N-1)&&(row==N-1)){
sudokuSolved=true;
}else{
//如果列是最后,就进入下一行
if(col==N-1) backtrack(row+1,0);
//否则就进入同行的下一个
else backtrack(row,col+1);
}
}
//回溯,删除数字
public void removeNumber(int d,int row,int col){
int idx=(row/n)*n+col/n;
rows[row][d]--;
columns[col][d]--;
boxes[idx][d]--;
board[row][col]='.';
}
}