陷入Sudoku回溯解算器(Java)

App*_*ath 5 java parameters recursion sudoku backtracking

我一直试图在Sudoku回溯解算器中弄清楚我的错误三天.问题来自leetcode Sudoku Solver.

我的解算器基于附图中的推论.问题是即使从root到leaf的路径无效,我的主板也会被更改.

换句话说,在经过无效路径后,它尝试的值将固定在我的原始板上.但是,我只更新我的原始板,当它的子节点返回true时(请参阅辅助方法中的部分://输入数字并生成子节点).

基本上,对于每个'.',我生成从1到9的所有可能性,构造一个填充当前''的临时板.有一种可能性,然后调用下一个'.'的助手.临时板.我知道因为空间成本而为每个可能的孩子存储相同尺寸的boardTemp并不好,但我现在主要关心的是在优化成本之前先解决问题.

总而言之,即使所有孩子都无效,为什么我的董事会会改变?

例如,基于初始板

..9748 ...; 7 ........; .2.1.9 ...;

..7 ... 24; .64.1.59; 0.98 ... 3 ..;

... 8.3.2; ........ 6; ... 2759 ..;

我跑完后得到了最终结果:

139748652; 745326189; 826159437;

35769.24; .64.1.59; 0.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;
}
Run Code Online (Sandbox Code Playgroud)

我的树回溯

小智 4

boardTemp使用和之间没有区别boardchar[][] 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] = '.';
    }
}
Run Code Online (Sandbox Code Playgroud)