题目:Write a program to solve a Sudoku puzzle by filling the empty cells.
Empty cells are indicated by the character ‘.’.
You may assume that there will be only one unique solution.
好几天没刷题了,主要是因为最近在忙着看深度学习方面的论文和实现,所以耽搁了下来==今天赶紧把HashTable系列的最后一道题目–Sudoku Solver给做了。
解决本题的方法就是回溯法或者递归法。接下来我们看一下。思路一就是对没有数字的地方进行填充,至于填充哪个数字我们使用一个函数IsValid来判断其是否满足数独的要求。这种方法的思想就是回溯法。每当填充一个数字就继续判断下一个,直到成功。或者出现失败,则回溯到上一个点,把其填充的数字变成’.’,并尝试其他数字。甚至以智慧推到原始状态==代码如下所示:
public void solveSudoku0(char[][] board) {
if(board == null || board.length == 0)
return;
solve(board);
}
private boolean solve(char[][] board){
//遍历整个数独的所有字符
for(int i=0; i<board.length; i++){
for(int j=0; j<board.length; j++){
//若该点为空,则进行填充
if(board[i][j] == '.'){
for(char c='1'; c<='9'; c++){
//选择要填充的数字
if(isValid(board, i, j, c)){
board[i][j] = c;
//填充完毕之后继续调用该函数填充下一个点
if(solve(board))
return true;
else
//如果solve(board)返回false,则说明前面的数字填充有误,将其还原。并判断下一个数字
board[i][j] = '.';
}
}
//如果1-9都无法满足则说明之前的数字填错了,回溯到原始状态
return false;
}
}
}
//直到所有的店=点都被填充完毕,返回true
return true;
}
private boolean isValid(char[][] board, int row, int col, char c){
for(int i = 0; i < 9; i++) {
if(board[i][col] != '.' && board[i][col] == c) return false; //check row
if(board[row][i] != '.' && board[row][i] == c) return false; //check column
if(board[3 * (row / 3) + i / 3][ 3 * (col / 3) + i % 3] != '.' &&
board[3 * (row / 3) + i / 3][3 * (col / 3) + i % 3] == c) return false; //check 3*3 block
}
return true;
}
上面的这种回退方法会重复的判断很多信息,导致执行效率比较低,只击败了30%的选手。接下来我们介绍另外一种方法,其可以击败95%的用户。其主要思路是使用3个二维数组来保存面板的行,列,九宫格信息,这样就省去了循环调用isValid函数进行判断的过程,大大的提高了函数执行的效率。代码入下:
int[][] map1; //map1, map2, map3 represents the row, coloum, grid check of suduku.
int[][] map2; //e.g. map1[2][3] means the number '4' has been existed in row 3.
int[][] map3;
char[][] board;
public void solveSudoku(char[][] board) {
this.board = board;
map1 = new int[9][9];
map2 = new int[9][9];
map3 = new int[9][9];
// initialization with the original board.
for(int i = 0; i<9; i++){
for(int j = 0; j<9; j++){
if(board[i][j]!='.'){
int num = board[i][j]-'0'-1;
map1[i][num] = 1;
map2[j][num] = 1;
map3[(i/3)*3+j/3][num] = 1;
}
}
}
solve(0);
}
private boolean solve(int count){
if(count>=81 ){ // if the pointer exceeds the end of the board,there is a solution.
return true;
}
for(int i = count; i<81; i++){ // the loop starts from the current call.
int ii = i/9; // transfer the 1 dimensional position to 2 dim position.
int jj = i%9;
if(board[ii][jj]!='.') continue;
else{
for(int k =0; k<9; k++){
// if there is a conflict, we choose the next number from 1 to 9.
if(map1[ii][k]==1||map2[jj][k]==1||map3[(ii/3)*3+jj/3][k]==1) continue;
else{
board[ii][jj] = (char)(k+49);
map1[ii][k] = 1;
map2[jj][k] = 1;
map3[3*(ii/3)+jj/3][k] = 1;
if(solve(ii*9+jj+1)==true) return true;
// If there is not a solution found in the next recursion call,
// then we recover, the current board and restriction arrays.
map1[ii][k] = 0;
map2[jj][k] = 0;
map3[3*(ii/3)+jj/3][k] = 0;
board[ii][jj] = '.';
}
}
// If there is no solution found from 1 to 9, then we go back to the last recursion call.
return false;
}
}
// This is trivial just to be correspond with the return type.
return true;
}

本文介绍了两种不同的方法来实现数独求解器。第一种方法采用回溯法,通过不断尝试并验证数字的正确性来填充空缺的单元格。第二种方法利用三个二维数组分别记录行列和九宫格的状态,以此避免重复验证,显著提高了解题速度。
187

被折叠的 条评论
为什么被折叠?



