数独题 是道比较费脑筋的题
Write a program to solve a Sudoku puzzle by filling the empty cells.
A sudoku solution must satisfy all of the following rules:
- Each of the digits
1-9
must occur exactly once in each row. - Each of the digits
1-9
must occur exactly once in each column. - Each of the the digits
1-9
must occur exactly once in each of the 93x3
sub-boxes of the grid.
Empty cells are indicated by the character '.'
.
A sudoku puzzle...
如图, 就是要把空出来的格子填数据, 并保证每行每列和每个框框中数字都不重复。 这道题没有任何取巧的办法, 唯一的方法就是让代码去填补每个空格, 填完后判断目前能否满足数独以及能否有机会填满数字后这个大正方形里的所有数字是否还有机会保持数独性质。(目前能否满足数独性质的话是很好判断的, 一个if语句对当前的大正方形进行一次整体性判断就可以了, 最难麻烦的是怎么判断填完当前数字后是否还有可能让整个大正方形填满数字后还保持数独性质, 思路的话是填了当前数字后继续递归执行填数字的代码。) 根据这两个判断我们进行代码的编写
public void solveSudoku(char[][] board) {
doSolve(board, 0, 0);
}
private boolean doSolve(char[][] board, int row, int col) {
for (int i = row; i < 9; i++, col = 0) { // note: must reset col here!
for (int j = col; j < 9; j++) {
if (board[i][j] != '.') continue;
for (char num = '1'; num <= '9'; num++) {
// 判断填完后是否满足数独性质
if (isValid(board, i, j, num)) {
board[i][j] = num;
/*首先看这个递归函数执行的地方。是在两层嵌套for循环内执行, 所以这个递归函数实际上执行了很多很多
遍, 就是说我填完了当前数字, 那么我执行这个递归, 他又会跑两层for循环, for循环里面就有数独性质
的判断和空格赋值的操作, 接下来解释i和j+1的原理, 填i的原因是减少了for循环的遍历层数, 为什么能这
么做呢, 如果想不清楚的话可以这样想, 如果i = 0和i = 1时的数字都是满的, 那么显然不需要对这两行数
字进行数独判断和赋值了, 这里只是举了一个特殊例子来帮助理解, 最好的话可以不通过特殊例子来理解i的
意义。 j+1这个数字其实是没有任何意义的, 因为每次递归后都被reset to 0;
*/
if (doSolve(board, i, j + 1))
return true;
board[i][j] = '.';
}
}
return false;
}
}
return true;
}
/* 一个是 i/3 一个是i%3 的操作我觉得也是很巧妙的,也应该解释一下,一个3*3的正方形。33 34 35,43,44,45,
53,54,55. 拿去对应就应该是这样操作,如果两个都是i/3或者i%3,那就不会获得这样的结果。
*/
private boolean isValid(char[][] board, int row, int col, char num) {
int blkrow = (row / 3) * 3, blkcol = (col / 3) * 3; // Block no. is i/3, first element is i/3*3
for (int i = 0; i < 9; i++)
if (board[i][col] == num || board[row][i] == num ||
board[blkrow + i / 3][blkcol + i % 3] == num)
return false;
return true;
}