编写一个程序,通过已填充的空格来解决数独问题。

一个数独的解法需遵循如下规则:

数字 1-9 在每一行只能出现一次。
数字 1-9 在每一列只能出现一次。
数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。


空白格用 '.' 表示。

JAVA程序设计:解数独(LeetCode:37)_实线

一个数独。

JAVA程序设计:解数独(LeetCode:37)_实线_02

答案被标成红色。

Note:

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

思路:如果枚举每一个格子应该放什么数字的话,呢复杂度就是9^81,因此我们需要考虑每次放一个数字时判断当前是否合法,也就是边填数字边进行约束。我们可以通过映射来存储当前行、当前列和当前子块是否已经存在该数字。这样有利于我们减少枚举的数字个数。我们可以采取回朔的方法进行数字的填写,如果当前数字不满足,则移除此位置上填的数字来继续枚举下一个数字。具体复杂度减少程度参考官方题解。

JAVA程序设计:解数独(LeetCode:37)_复杂度_03

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);
    }
}