编写一个程序,通过已填充的空格来解决数独问题。
一个数独的解法需遵循如下规则:
数字 1-9 在每一行只能出现一次。
数字 1-9 在每一列只能出现一次。
数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。
空白格用 '.' 表示。
一个数独。
答案被标成红色。
Note:
给定的数独序列只包含数字 1-9 和字符 '.' 。
你可以假设给定的数独只有唯一解。
给定数独永远是 9x9 形式的。
思路:如果枚举每一个格子应该放什么数字的话,呢复杂度就是9^81,因此我们需要考虑每次放一个数字时判断当前是否合法,也就是边填数字边进行约束。我们可以通过映射来存储当前行、当前列和当前子块是否已经存在该数字。这样有利于我们减少枚举的数字个数。我们可以采取回朔的方法进行数字的填写,如果当前数字不满足,则移除此位置上填的数字来继续枚举下一个数字。具体复杂度减少程度参考官方题解。
class Solution {
int n=3;
int N=n*n;
int [][] rows=new int[N][N + 1];
int [][] columns=new int[N][N + 1];
int [][] boxes=new int[N][N + 1];
char[][] board;
boolean isOk=false;
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 placeNumber(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]=(char)(d+'0');
}
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]='.';
}
public void placeNextNumber(int row,int col)
{
if(row==N-1 && col==N-1)
isOk=true;
else
{
if(col==N-1)
backTrack(row+1,0);
else
backTrack(row,col+1);
}
}
public void backTrack(int row,int col)
{
if(board[row][col]=='.')
{
for(int i=1;i<10;i++)
{
if(couldPlace(i,row,col))
{
placeNumber(i,row,col);
placeNextNumber(row,col);
if(!isOk)
removeNumber(i,row,col);
}
}
}
else
placeNextNumber(row,col);
}
public void solveSudoku(char[][] board) {
this.board=board;
for(int i=0;i<N;i++)
for(int j=0;j<N;j++)
{
char num=board[i][j];
if(num=='.')
continue;
int d=Character.getNumericValue(num);
placeNumber(d,i,j);
}
backTrack(0,0);
}
}