2017-10-29 Updated(不知道为啥这个编辑器在后面用拼音输入法就会崩溃,应该是和代码编辑器有关):
下面贴一个会超时的答案:
public void solveSudoku(char[][] board) {
// rowValidate[x][y] = true的意思是说在第x行,值y已经出现过了
boolean[][] rowValidate = new boolean[9][9];
boolean[][] columnValidate = new boolean[9][9];
boolean[][] chunkValidate = new boolean[9][9];
_solveSudokuBacktrack(board, 0, rowValidate, columnValidate, chunkValidate);
}
private boolean _solveSudokuBacktrack(char[][] board, int position, boolean[][] rowValidate, boolean[][] columnValidate, boolean[][] chunkValidate) {
if (position == 81) {
return true;
}
int row = position / 9;
int column = position % 9;
int chunk = column / 3 + (row / 3) * 3;
boolean result = false;
if (board[row][column] != '.') {
int val = (int)board[row][column] - '1';
if (!rowValidate[row][val] && !columnValidate[column][val] && !chunkValidate[chunk][val]) {
rowValidate[row][val] = columnValidate[column][val] = chunkValidate[chunk][val] = true;
result = _solveSudokuBacktrack(board, position + 1, rowValidate, columnValidate, chunkValidate);
rowValidate[row][val] = columnValidate[column][val] = chunkValidate[chunk][val] = false;
}
} else {
for (int val = 0; val < 9 && !result; val++) {
if (!rowValidate[row][val] && !columnValidate[column][val] && !chunkValidate[chunk][val]) {
rowValidate[row][val] = columnValidate[column][val] = chunkValidate[chunk][val] = true;
board[row][column] = (char)('1' + val);
result |= _solveSudokuBacktrack(board, position + 1, rowValidate, columnValidate, chunkValidate);
if (result) {
break;
}
board[row][column] = '.';
rowValidate[row][val] = columnValidate[column][val] = chunkValidate[chunk][val] = false;
}
}
}
return result;
}
这是我刚才写的,咋一看和之前我给的正确答案差别不大。但是这里最关键的问题在于在最开始我并没有给全局的数据进行初始化,也就是下面答案的最开始for循环。导致validation不能filter掉很多cases,之后branch cases被无限扩大。最后很明显也是可以得到正确答案的,但是实在是虚耗了很多很多的计算。
所以稍微修改一下。得到下述正解:
public void solveSudoku(char[][] board) {
// rowValidate[x][y] = true的意思是说在第x行,值y已经出现过了
boolean[][] rowValidate = new boolean[9][9];
boolean[][] columnValidate = new boolean[9][9];
boolean[][] chunkValidate = new boolean[9][9];
for(int i = 0; i < 9; i++){
for(int j = 0; j < 9; j++){
if(board[i][j] == '.')
continue;
rowValidate[i][board[i][j] - '1'] = true;
columnValidate[j][board[i][j] - '1'] = true;
chunkValidate[j / 3 + i / 3 * 3][board[i][j] - '1'] = true;
}
}
_solveSudokuBacktrack(board, 0, rowValidate, columnValidate, chunkValidate);
}
private boolean _solveSudokuBacktrack(char[][] board, int position, boolean[][] rowValidate, boolean[][] columnValidate, boolean[][] chunkValidate) {
if (position == 81) {
return true;
}
int row = position / 9;
int column = position % 9;
int chunk = column / 3 + (row / 3) * 3;
boolean result = false;
if (board[row][column] != '.') {
result = _solveSudokuBacktrack(board, position + 1, rowValidate, columnValidate, chunkValidate);
} else {
for (int val = 0; val < 9 && !result; val++) {
if (!rowValidate[row][val] && !columnValidate[column][val] && !chunkValidate[chunk][val]) {
rowValidate[row][val] = columnValidate[column][val] = chunkValidate[chunk][val] = true;
result |= _solveSudokuBacktrack(board, position + 1, rowValidate, columnValidate, chunkValidate);
if (result) {
board[row][column] = (char)('1' + val);
}
rowValidate[row][val] = columnValidate[column][val] = chunkValidate[chunk][val] = false;
}
}
}
return result;
}
问题链接:https://oj.leetcode.com/problems/sudoku-solver/
问题描述:看链接,图多懒得贴。。。
问题API:public void solveSudoku(char[][] board)
问题分析:
这题其实就是和N-queens一样,valid判断和往下递归和traceback。
先给的做法就比较常规,每次都会做判断,往下递归的层级也是有row和column来表示。
public void solveSudoku(char[][] board) {
solveSudoku(board, 0, 0);
}
public boolean solveSudoku(char[][] board, int col, int row){
if(row >= 9)
return solveSudoku(board, col + 1, 0);
if(col >= 9)
return true;
if(board[col][row] != '.')
return solveSudoku(board, col, row + 1);
for(int k = 1; k <= 9; k++){
if(isValid(board, col, row, k)){//先做validaty的判断,然后再往下递归。
board[col][row] = (char)(k +'0');
if(solveSudoku(board, col, row)){
return true;
}
board[col][row] = '.';
}
}
return false;
}
public boolean isValid(char[][] board, int col, int row, int num){//这个就和N-queen里面的合理性判断一样
//boolean[] rowMarked = new boolean[10];
//verifying row number;
for(int i = 0; i < board[0].length; i++){
if(board[col][i] == '.')
continue;
if(board[col][i] - '0' == num)
return false;
}
//verifying column number;
boolean[] colMarked = new boolean[10];
for(int i = 0; i < board.length; i++){
if(board[i][row] == '.'){
continue;
}
if(board[i][row] - '0' == num)
return false;
}
//verifying 3x3;
int colstart = (col / 3) * 3;
int rowstart = (row / 3) * 3;
for(int i = 0; i < 3; i++){
for(int j = 0; j < 3; j++){
if(board[colstart + i][rowstart + j] == '.')
continue;
int idx = board[colstart + i][rowstart + j] - '0';
if(idx == num)
return false;
}
}
board[col][row] = (char)(num + '0');
return true;
}
另一个做法就相对有效率一些了,用一个0~80的int pos来表示递归的层级和当前的位置,另外用三个boolean[9][9]来实现preprocess了validation,这样子计算效率也提高了很多。分别是rowcheck[9][9],columncheck[9][9],matcheck[9][9],第一维的index表示的是第几个row/column/mat,第二维的index表示1~9的数字是否出现过。当pos到了81,就表示全部东西都已经遍历过了,可以返回true了。
public void solveSudoku(char[][] board) {
boolean[][] row_check = new boolean[9][9];
boolean[][] col_check = new boolean[9][9];
boolean[][] mat_check = new boolean[9][9];
for(int i = 0; i < 9; i++){
for(int j = 0; j < 9; j++){
if(board[i][j] == '.')
continue;
row_check[i][board[i][j] - '1'] = true;
col_check[j][board[i][j] - '1'] = true;
mat_check[j / 3 + i / 3 * 3][board[i][j] - '1'] = true;
}
}
helper(0, board, row_check, col_check, mat_check);
}
public boolean helper(int pos, char[][] board, boolean[][] row_check, boolean[][] col_check, boolean[][] mat_check){
if(pos == 81)
return true;
int row = 0, col = 0;
while(pos < 81){
col = pos % 9;
row = pos / 9;
if(board[row][col] == '.')
break;
pos++;
}
if(pos == 81)
return true;
for(char c = '1'; c <= '9'; c++){
if(!row_check[row][c - '1'] && !col_check[col][c - '1'] && !mat_check[col / 3 + row / 3 * 3][c - '1']){
row_check[row][c - '1'] = true;
col_check[col][c - '1'] = true;
mat_check[col / 3 + row / 3 * 3][c - '1'] = true;
board[row][col] = c;
if(helper(pos + 1, board, row_check, col_check, mat_check))
return true;
board[row][col] = '.';
row_check[row][c - '1'] = false;
col_check[col][c - '1'] = false;
mat_check[col / 3 + row / 3 * 3][c - '1'] = false;
}
}
return false;
}