Leetcode: Sudoku Solver
Write a program to solve a Sudoku puzzle by filling the empty cells. Empty cells are indicated by the character '.'. You may assume that there will be only one unique solution.
又是一道NP的问题,这种问题时间复杂度就不用考虑了,肯定是指数量级的,参考了一下别人的思路,这道题的思路与N-Queens,Permutations问题比较相似, 简单地说思路就是循环处理子问题,对于每个格子,带入不同的9个数,然后判合法,如果成立就递归继续。判合法可以用Valid Sudoku做为subroutine,但是其实在这里因为每次进入时已经保证之前的board不会冲突,所以不需要判断整个盘,只需要看当前加入的数字和之前是否冲突就可以,这样可以大大提高运行效率,毕竟判合法在程序中被多次调用。
第二遍做法:
1 public class Solution { 2 public void solveSudoku(char[][] board) { 3 if (board == null || board.length != 9 || board[0].length != 9) return; 4 helper(board, 0, 0); 5 } 6 7 public boolean helper(char[][] board, int i, int j) { 8 if (j == 9) return helper(board, i+1, 0); 9 if (i == 9) return true; 10 if (board[i][j] != '.') return helper(board, i, j+1); 11 for (int k=0; k<9; k++) { 12 board[i][j] = (char)('1' + k); 13 if (check(board, i, j)) { 14 if (helper(board, i, j+1)) return true; 15 } 16 } 17 board[i][j] = '.'; 18 return false; 19 } 20 21 public boolean check(char[][] board, int i, int j) { 22 for (int t=0; t<9; t++) { 23 if (t != i && board[t][j] == board[i][j]) return false; 24 } 25 for (int t=0; t<9; t++) { 26 if (t != j && board[i][t] == board[i][j]) return false; 27 } 28 for (int s=i/3*3; s<i/3*3+3; s++) { 29 for (int t=j/3*3; t<j/3*3+3; t++) { 30 if (s!=i || t!=j) { 31 if(board[s][t] == board[i][j]) return false; 32 } 33 } 34 } 35 return true; 36 } 37 }
17行一定要记得在return false之前把board[i][j] 改回‘.’, 否则会出很严重的错误:比如board[i][j+1]把1~9都试过都不是valid的,这时候要回到board[i][j]去换另一个数试一试,再进入board[i][j+1],由于刚才已经把它改成1~9了而且没有改回‘.’,所以递归就会沿着一条错误的路走下去。最后会得到一组明显错误的答案(里面很多重复)
1 public class Solution { 2 public void solveSudoku(char[][] board) { 3 if (board == null || board.length != 9 || board[0].length != 9) return; 4 helper(board, 0, 0); 5 } 6 7 public boolean helper(char[][] board, int i, int j) { 8 if (j >= 9) return helper(board, i+1, 0); 9 if (i >= 9) return true; 10 if (board[i][j] == '.') { 11 for (int k = 1; k <= 9; k++) { 12 board[i][j] = (char)('0' + k); 13 if (isvalid(board, i, j)) { 14 if(helper(board, i, j+1)) 15 return true; 16 } 17 board[i][j] = '.'; 18 } 19 } 20 else { 21 return helper(board, i, j+1); 22 } 23 return false; 24 } 25 26 public boolean isvalid(char[][] board, int i, int j) { 27 for (int a = 0; a < 9; a++) { 28 if (a != i && board[a][j] == board[i][j]) return false; 29 } 30 31 for (int b = 0; b < 9; b++) { 32 if (b != j && board[i][b] == board[i][j]) return false; 33 } 34 35 for (int c = i/3*3; c < i/3*3 + 3; c++) { 36 for (int d = j/3*3; d < j/3*3 + 3; d++) { 37 if ((c != i || d != j) && board[c][d] == board[i][j]) return false; 38 } 39 } 40 41 return true; 42 } 43 }
在具体编程的时候,还有一个细节问题需要注意,那就是37行 (c != i || d != j)这个操作一定要用括号括起来,否则&&的操作优先级比||高,会出错