编写一个程序,通过已填充的空格来解决数独问题。
一个数独的解法需遵循如下规则:
- 数字 1-9 在每一行只能出现一次。
- 数字 1-9 在每一列只能出现一次。
- 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。 空白格用 '.' 表示。
一个数独。
答案被标成红色。
Note:
- 给定的数独序列只包含数字 1-9 和字符 '.' 。
- 你可以假设给定的数独只有唯一解。
- 给定数独永远是 9x9 形式的。
解题思路 :
总共有81个元素,按行顺序处理,遇到空白格,则依次尝试填入1-9的数字,判断填入之后是否为有效的数独(可调用LeetCode-36.有效的数独的的函数),若为有效的数独,则继续处理后续的空白格(处理方式仍然是依次尝试填入1-9的数字,然后判断数独是否有效),当遇到无效的数独时,则回溯,所有元素处理完,则数独解完。
这种解法有两个非常耗时的地方:
- 遇到空白格,依次尝试1-9的数字,会做很多重复运算,比如第一行已经有5、 3,遇到空白格其实就不必尝试再填入5、 3
- 每次在空白格填入一个数字之后,都需要判断一遍当前数独是否有效
优化:用三个boolean型的二维数组l[][]、c[][]、s[][],
- l[i][j]表示第i行是否已经出现j+1这个数字
- c[i][j]表示第i列是否已经出现j+1这个数字
- s[i][j]表示第i个3x3九宫格是否已经出现j+1这个数字
并且每次遇到空白格时,依次尝试1-9的数字前,判断空白格所属的行、列以及3x3的九宫格是否已经出现该数字,若出现过,则直接跳过(剪枝),便可保证每次填入数字之后的数独都是有效的,无需再判断。
代码如下:
class Solution {
public void solveSudoku(char[][] board) {
boolean[][] l = new boolean[9][9];//记录第i行是否已包含j+1字符
boolean[][] c = new boolean[9][9];//记录第i列是否已包含j+1字符
boolean[][] s = new boolean[9][9];//记录第i个九宫格是否已包含j+1字符
for(int line = 0; line < 9; line++){
for(int col = 0; col < 9; col++){
if('.' == board[line][col])
continue;
l[line][board[line][col] - '1'] = true;
c[col][board[line][col] - '1'] = true;
s[line/3*3 + col/3][board[line][col] - '1'] = true;
}
}
solve(board, 0, l, c, s);
}
private boolean solve(char[][] board, int position, boolean[][] l, boolean[][] c, boolean[][] s){
int index = position;
for(; index < 81 && '.' != board[index/9][index%9]; index++);
if(index < 81){
int line = index/9;
int col = index%9;
for(int i = 1; i < 10; i++){
if(l[line][i - 1] || c[col][i - 1] || s[line/3*3 + col/3][i - 1])
continue;
board[line][col] = (char)('0' + i);
l[line][i - 1] = true;
c[col][i - 1] = true;
s[line/3*3 + col/3][i - 1] = true;
if(solve(board, index+1, l, c, s))
return true;
board[line][col] = '.';
l[line][i - 1] = false;
c[col][i - 1] = false;
s[line/3*3 + col/3][i - 1] = false;
}
return false;
}
return true;
}
}