37. Sudoku Solver(二维回溯)
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...
...and its solution numbers marked in red.
class Solution: def solveSudoku(self, board: List[List[str]]) -> None: """ Do not return anything, modify board in-place instead. """ def backtrack(board: List[List[str]], i: int, j: int) -> bool: if j == 9: # 穷举到最后一列的话就换到下一行重新开始。 return backtrack(board, i + 1, 0) if i == 9: # 找到一个可行解,触发 base case return True if board[i][j] != '.': # 如果有预设数字,不用我们穷举 return backtrack(board, i, j + 1) for ch in range(1,10): ch = str(ch) # 如果遇到不合法的数字,就跳过 if is_vaild(board, i, j, ch): board[i][j] = ch # 如果找到一个可行解,立即结束 if backtrack(board, i, j + 1): return True board[i][j] = '.' # 穷举完 1~9,依然没有找到可行解,此路不通 return False def is_vaild(board,i,j,val): for a in range(9): if board[i][a] == val: return False for a in range(9): if board[a][j] == val: return False ki = i//3 * 3 kj = j//3 * 3 for a in range(3): for b in range(3): if board[ki+a][kj+b] == val: return False return True backtrack(board,0,0)
怎么做二维递归呢?
本题就不一样了,本题中棋盘的每一个位置都要放一个数字,并检查数字是否合法,解数独的树形结构要比N皇后更宽更深。
N 皇后 只要判断放不放东西。数独需要考虑放哪个数字。
class Solution { public: bool backtrack(vector<vector<char>>& board) { for(int level = 0; level < board.size(); ++level) { for (int index = 0; index < board[0].size();++index) { if (board[level][index] != '.') continue; for (char val = '1';val <='9';val++) { if(valid(board,level,index,val)) { board[level][index] = val; if(backtrack(board)) return true; board[level][index] = '.'; } } return false; } } return true; } bool valid(vector<vector<char>>& board, int level, int index, char val) { // 检查列 for(int i = 0; i < 9 ;++i) { if (board[i][index] == val ) { return false; } } // 检查行 for (int i = 0;i < 9; ++i) { if (board[level][i] == val) { return false; } } // 检查小方格 int start_level = (level /3) *3; int start_index = (index /3) *3; for(int i = start_level; i < start_level+3;++i ) { for(int j = start_index; j <start_index+3;++j) { if (board[i][j] == val) { return false; } } } return true; } void solveSudoku(vector<vector<char>>& board) { backtrack(board); } };
如果当前位置是空,则尝试1到9中所有的数字,如果对于1到9中的某些数字,当前是合法的,则继续尝试下一个位置(调用自身)。
1 public class Solution { 2 public void solveSudoku(char[][] board) { 3 if(board == null || board.length == 0) 4 return; 5 solve(board); 6 } 7 8 public boolean solve(char[][] board){ 9 for(int i = 0; i < board.length; i++){ 10 for(int j = 0; j < board[0].length; j++){ 11 if(board[i][j] == '.'){ 12 for(char c = '1'; c <= '9'; c++){//trial. Try 1 through 9 13 if(isValid(board, i, j, c)){ 14 board[i][j] = c; //Put c for this cell 15 16 if(solve(board)) 17 return true; //If it's the solution return true 18 else 19 board[i][j] = '.'; //Otherwise go back 20 } 21 } 22 23 return false; 24 } 25 } 26 } 27 return true; 28 } 29 30 private boolean isValid(char[][] board, int row, int col, char c){ 31 for(int i = 0; i < 9; i++) { 32 if(board[i][col] != '.' && board[i][col] == c) return false; //check row 33 if(board[row][i] != '.' && board[row][i] == c) return false; //check column 34 } 35 for(int i = 3*(row/3);i<3*(row/3+1);i++) 36 for(int j = 3*(col/3);j<3*(col/3+1);j++) 37 if(board[i][j] != '.' && board[i][j] == c) return false; //check 3*3 block 38 39 return true; 40 } 41 }