卡在数独回溯求解器中 (Java)
Stuck in Sudoku backtracking solver (Java)
三天来我一直在试图找出我在数独回溯求解器中的错误。题目出自leetcodeSudoku Solver。
我的求解器是根据附图推导的。问题是即使从根到叶的路径无效,我的板也会被更改。
换句话说,它经过无效路径后,它尝试的值在我的原始板中是固定的。但是,我只在 children returns 为真时更新我的原始板(请参阅辅助方法中的部分:// put a number and generate children)。
基本上,对于每个“.”,我生成从 1 到 9 的所有可能性,构建一个临时板来填充当前的“.”。有一种可能性,然后调用下一个 '.' 的助手带温度板。我知道由于 space 的成本,为每个可能的 child 存储相同大小的 boardTemp 是不好的,但我现在主要关心的是在优化成本之前先解决问题。
总而言之,为什么我的板子在所有 children 都无效的情况下仍在更改?
例如以初始板为基础
..9748...; 7.......; .2.1.9...;
..7...24.; .64.1.59.; .98...3..;
...8.3.2.; .........6; ...2759..;
我运行得到了最终结果:
139748652; 745326189; 826159437;
35769.24.; .64.1.59.; .98...3..;
...8.3.2.; .........6; ...2759..;
public void sudokuSolver(char[][] board) {
for (int i = 0 ; i < board.length ; i++) {
for (int j = 0 ; j < board.length ; j++) {
if (board[i][j] == '.') {
// find the first '.' as root
helper(i, j, board);
return;
}
}
}
}
private boolean helper(int row, int col, char[][] board) {
// case 2. check if it has following '.' and store its position
boolean hasNext = false;
boolean nextSearching = false;
int nextRow = row;
int nextCol = col;
for (int i = 0 ; i < board.length ; i++) {
for (int j = 0; j < board.length ; j++) {
if (nextSearching && !hasNext && board[i][j] == '.') {
hasNext = true; // there is next!
nextRow = i;
nextCol = j;
}
if (i == row && j == col) {
nextSearching = true;
}
}
}
// exit condition: last '.'
if (!hasNext) {
for (char put = '1' ; put <= '9' ; put ++) {
if (isValid(row, col, board, put)) {
return true;
}
}
return false;
}
// put a number and generate children
for (char put = '1' ; put <= '9' ; put ++) {
if (isValid(row, col, board, put)) {
char[][] boardTemp = board;
boardTemp[row][col] = put;
boolean valid = helper(nextRow, nextCol, boardTemp);
if (valid) {
// board is supposed to change only when valid is true.
board[row][col] = put;
return true;
}
}
}
return false;
}
private boolean isValid(int row, int col, char[][] board, char c) {
// go through each row, column, and subblock to determine if c is a valid choice based on current board.
for (int jCol = 0 ; jCol < 9 ; jCol ++) {
if (board[row][jCol] == c) {
return false;
}
}
for (int iRow = 0 ; iRow < 9 ; iRow ++) {
if (board[iRow][col] == c) {
return false;
}
}
for (int i = row/3*3 ; i < row/3*3 + 3 ; i++) {
for (int j = col/3*3 ; j < col/3*3 + 3 ; j++) {
if (board[i][j] == c) {
return false;
}
}
}
return true;
}
使用boardTemp
和board
没有区别。 char[][] boardTemp = board
表示它们指向相同的内存...您在原始代码中遗漏的是输入无效数字后的 else
部分:
for (char put = '1' ; put <= '9' ; put ++) {
if (isValid(row, col, board, put)) {
char[][] boardTemp = board; // board and boardTemp are essentially the same thing
boardTemp[row][col] = put;
boolean valid = helper(nextRow, nextCol, boardTemp);
if (valid) {
// board is supposed to change only when valid is true.
board[row][col] = put;
return true;
}
// THIS IS WHAT YOU MISSED!!
// if you don't reset the '.' back, your later backtrack will not work
// because you put an invalid number on your board and you will never find a solution
boardTemp[row][col] = '.';
}
}
三天来我一直在试图找出我在数独回溯求解器中的错误。题目出自leetcodeSudoku Solver。
我的求解器是根据附图推导的。问题是即使从根到叶的路径无效,我的板也会被更改。
换句话说,它经过无效路径后,它尝试的值在我的原始板中是固定的。但是,我只在 children returns 为真时更新我的原始板(请参阅辅助方法中的部分:// put a number and generate children)。
基本上,对于每个“.”,我生成从 1 到 9 的所有可能性,构建一个临时板来填充当前的“.”。有一种可能性,然后调用下一个 '.' 的助手带温度板。我知道由于 space 的成本,为每个可能的 child 存储相同大小的 boardTemp 是不好的,但我现在主要关心的是在优化成本之前先解决问题。
总而言之,为什么我的板子在所有 children 都无效的情况下仍在更改?
例如以初始板为基础
..9748...; 7.......; .2.1.9...;
..7...24.; .64.1.59.; .98...3..;
...8.3.2.; .........6; ...2759..;
我运行得到了最终结果:
139748652; 745326189; 826159437;
35769.24.; .64.1.59.; .98...3..;
...8.3.2.; .........6; ...2759..;
public void sudokuSolver(char[][] board) {
for (int i = 0 ; i < board.length ; i++) {
for (int j = 0 ; j < board.length ; j++) {
if (board[i][j] == '.') {
// find the first '.' as root
helper(i, j, board);
return;
}
}
}
}
private boolean helper(int row, int col, char[][] board) {
// case 2. check if it has following '.' and store its position
boolean hasNext = false;
boolean nextSearching = false;
int nextRow = row;
int nextCol = col;
for (int i = 0 ; i < board.length ; i++) {
for (int j = 0; j < board.length ; j++) {
if (nextSearching && !hasNext && board[i][j] == '.') {
hasNext = true; // there is next!
nextRow = i;
nextCol = j;
}
if (i == row && j == col) {
nextSearching = true;
}
}
}
// exit condition: last '.'
if (!hasNext) {
for (char put = '1' ; put <= '9' ; put ++) {
if (isValid(row, col, board, put)) {
return true;
}
}
return false;
}
// put a number and generate children
for (char put = '1' ; put <= '9' ; put ++) {
if (isValid(row, col, board, put)) {
char[][] boardTemp = board;
boardTemp[row][col] = put;
boolean valid = helper(nextRow, nextCol, boardTemp);
if (valid) {
// board is supposed to change only when valid is true.
board[row][col] = put;
return true;
}
}
}
return false;
}
private boolean isValid(int row, int col, char[][] board, char c) {
// go through each row, column, and subblock to determine if c is a valid choice based on current board.
for (int jCol = 0 ; jCol < 9 ; jCol ++) {
if (board[row][jCol] == c) {
return false;
}
}
for (int iRow = 0 ; iRow < 9 ; iRow ++) {
if (board[iRow][col] == c) {
return false;
}
}
for (int i = row/3*3 ; i < row/3*3 + 3 ; i++) {
for (int j = col/3*3 ; j < col/3*3 + 3 ; j++) {
if (board[i][j] == c) {
return false;
}
}
}
return true;
}
使用boardTemp
和board
没有区别。 char[][] boardTemp = board
表示它们指向相同的内存...您在原始代码中遗漏的是输入无效数字后的 else
部分:
for (char put = '1' ; put <= '9' ; put ++) {
if (isValid(row, col, board, put)) {
char[][] boardTemp = board; // board and boardTemp are essentially the same thing
boardTemp[row][col] = put;
boolean valid = helper(nextRow, nextCol, boardTemp);
if (valid) {
// board is supposed to change only when valid is true.
board[row][col] = put;
return true;
}
// THIS IS WHAT YOU MISSED!!
// if you don't reset the '.' back, your later backtrack will not work
// because you put an invalid number on your board and you will never find a solution
boardTemp[row][col] = '.';
}
}