Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Stuck in Sudoku backtracking solver (Java)

Tags:

I have been trying to figure out my mistake in the Sudoku backtracking solver for three days. The problem is from leetcode Sudoku Solver.

My solver is based on the deduction in the attached picture. The problem is that my board is changed even if a path from root to leaf is invalid.

In other words, after it goes through an invalid path, the values it attempted are fixed in my original board. However, I only update my original board when its children returns true ( see the part in helper method: // put a number and generate children).

Basically, for each '.', I generate all possibilities from 1 to 9, construct a temp board which fills the current '.' with one possibility, then call helper of the next '.' with temp board. I know it is not good to store a same size boardTemp for each possible child because of space cost, but my main concern now is to solve the problem first before optimizing the cost.

All in all, why is my board changing even if all children is not valid?

For example, base on the initial board

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

..7...24.; .64.1.59.; .98...3..;

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

I got the final result after I run:

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;
}

My tree for backtracking

like image 486
Appalachian Math Avatar asked Aug 10 '16 06:08

Appalachian Math


1 Answers

There is no difference between using boardTemp and board. char[][] boardTemp = board means they point to the same memory... What you missed in your original code is the else part in after you put an invalid number:

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] = '.';
    }
}
like image 118
Shawn Song Avatar answered Sep 23 '22 16:09

Shawn Song