37. 解数独
编写一个程序,通过填充空格来解决数独问题。
数独的解法需 遵循如下规则:
- 数字
1-9
在每一行只能出现一次。 - 数字
1-9
在每一列只能出现一次。 - 数字
1-9
在每一个以粗实线分隔的3x3
宫内只能出现一次。(请参考示例图)
数独部分空格内已填入了数字,空白格用 '.'
表示。
示例:
输入:board = [["5","3",".",".","7",".",".",".","."],["6",".",".","1","9","5",".",".","."],[".","9","8",".",".",".",".","6","."],["8",".",".",".","6",".",".",".","3"],["4",".",".","8",".","3",".",".","1"],["7",".",".",".","2",".",".",".","6"],[".","6",".",".",".",".","2","8","."],[".",".",".","4","1","9",".",".","5"],[".",".",".",".","8",".",".","7","9"]]
输出:[["5","3","4","6","7","8","9","1","2"],["6","7","2","1","9","5","3","4","8"],["1","9","8","3","4","2","5","6","7"],["8","5","9","7","6","1","4","2","3"],["4","2","6","8","5","3","7","9","1"],["7","1","3","9","2","4","8","5","6"],["9","6","1","5","3","7","2","8","4"],["2","8","7","4","1","9","6","3","5"],["3","4","5","2","8","6","1","7","9"]]
解释:输入的数独如上图所示,唯一有效的解决方案如下所示:
提示:
board.length == 9
board[i].length == 9
board[i][j]
是一位数字或者'.'
- 题目数据 保证 输入数独仅有一个解
class Solution {
public void solveSudoku(char[][] board) {
// 九宫格的九个基点
Map<Integer, int[]> map = new HashMap<Integer, int[]>(){{
put(0, new int[]{0,0});
put(1, new int[]{0,3});
put(2, new int[]{0,6});
put(3, new int[]{3,0});
put(4, new int[]{3,3});
put(5, new int[]{3,3});
put(6, new int[]{6,0});
put(7, new int[]{6,3});
put(8, new int[]{6,6});
}};
char[][] result = new char[9][9];
huisu(board, 0, 0, map, result);
// 记录下答案,这是正确的
for(int m = 0; m < 9; ++m){
for(int n = 0; n < 9; ++n) {
board[m][n] = result[m][n];
}
}
}
public void huisu(char[][] board, int x, int y, Map<Integer, int[]> map, char[][] result){
// 最后一步
if(x == 8 && y == 8){
if(board[y][x] == '.'){
// 最后填入一个数字,不需要在往深处走
for(int i = 1; i <= 9; ++i){
// 先改为数字
board[y][x] = (char)(i + 48);
if(isValidSudoku(board, x, y, map)){
// 记录下答案,这是正确的
for(int m = 0; m < 9; ++m){
for(int n = 0; n < 9; ++n){
result[m][n] = board[m][n];
}
}
return;
}
// 回溯
board[y][x] = '.';
}
} else {
// 记录下答案,这是正确的
for(int m = 0; m < 9; ++m){
for(int n = 0; n < 9; ++n){
result[m][n] = board[m][n];
}
}
return;
}
}
// 说明是要填入的
if(board[y][x] == '.'){
for(int i = 1; i <= 9; ++i){
// 先改为数字
board[y][x] = (char)(i + 48);
// 如果是正确的,继续填入下一个
// 如果不正确的就直接不用进行下一步了,并且会一直回溯到上一个正确的点,输入下一个数字。
if(isValidSudoku(board, x, y, map)){
huisu(board, (x + 1) % 9, y + ((x + 1) / 9), map, result);
}
// 回溯
board[y][x] = '.';
}
} else {
huisu(board, (x + 1) % 9, y + ((x + 1) / 9), map, result);
}
}
public boolean isValidSudoku(char[][] board, int x, int y, Map<Integer, int[]> map) {
// 先根据传入的xy去判断处于第几个九宫格,获取对应的基点坐标
int[] ints = map.get(x / 3 + y / 3 * 3);
// result:比对数据的数组
int[] result = new int[9];
// temp:暂时存放数据。
int temp;
// 能走出验证横线的循环说明横线没有问题,接下来刷新result数组,重新验证竖线
for(int m = 0; m < 9; m++){
if(board[y][m] == '.'){
continue;
}
temp = board[y][m] - '1';
// 说明之前已经加过了,即出现了重复
if(result[temp] > 0){
return false;
}
result[temp]++;
}
for(int i = 0; i < 9; ++i){
result[i] = 0;
}
// 能走出竖线的循环说明竖线没有问题,接下来刷新result数组,重新验证九宫格
for(int m = 0; m < 9; ++m){
if(board[m][x] == '.'){
continue;
}
temp = board[m][x] - '1';
if(result[temp] > 0){
return false;
}
result[temp]++;
}
for(int i = 0; i < 9; ++i){
result[i] = 0;
}
y = ints[0];
x = ints[1];
// 能走出九宫格循环说明九宫格没有问题
for(int m = 0; m < 3; ++m){
for(int n = 0; n < 3; ++n){
if(board[y + m][x + n] == '.'){
continue;
}
temp = board[y + m][x + n] - '1';
if(result[temp] > 0){
return false;
}
result[temp]++;
}
}
return true;
}
}