LeetCode: solveSudoku 解题报告

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.

SOLUTION:

ref: http://blog.csdn.net/fightforyourdream/article/details/16916985

采用递归+回溯模板来解决此问题:

1. 判定DFS的退出条件。

    (1) y越界,应该往下一行继续解。

  (2)x越界,代表数独解完,应该返回。

2. DFS的主体部分:

    把9个可能的值遍历一次,如果是OK的(使用独立 的Valid函数来判定行,列,BLOCK是否符合),继续DFS,否则回溯。

3. 最后的返回值:

    如果所有的可能性遍历都没有找到TRUE,最后应该返回FALSE,也就是当前情况无解。这个时候DFS会自动回溯到上一层再查找别的可能解。

 1 public static void main(String[] args) {  

 2         char[][] board = {  

 3                 {'.','.','9','7','4','8','.','.','.'},  

 4                 {'7','.','.','.','.','.','.','.','.'},  

 5                 {'.','2','.','1','.','9','.','.','.'},  

 6                 {'.','.','7','.','.','.','2','4','.'},  

 7                 {'.','6','4','.','1','.','5','9','.'},  

 8                 {'.','9','8','.','.','.','3','.','.'},  

 9                 {'.','.','.','8','.','3','.','2','.'},  

10                 {'.','.','.','.','.','.','.','.','6'},  

11                 {'.','.','.','2','7','5','9','.','.'},  

12         };  

13         solveSudoku(board);  

14         for(int i=0; i<9; i++){  

15             for(int j=0; j<9; j++){  

16                 System.out.print(board[i][j]);  

17             }  

18             System.out.println();  

19         }  

20     }  

21     

22     public void solveSudoku1(char[][] board) {

23         dfs1(board, 0, 0);

24     }

25     

26     public boolean dfs1(char[][] board, int x, int y) {

27         // go to next row.

28         if (y == 9) {

29             y = 0;

30             x++;

31         }

32         

33         // done

34         if (x >= 9) {

35             return true;

36         }

37         

38         // Skip the solved point.

39         if (board[x][y] != '.') {

40             return dfs1(board, x, y + 1);

41         }

42         

43         // Go throught all the possibilities.

44         for (int k = 0; k < 9; k++) {

45             board[x][y] = (char)('1' + k);    

46             // SHOULD RETURN HERE IF INVALID.                

47             if (isValid1(board, x, y) && dfs1(board, x, y  + 1)) {

48                 return true;

49             }

50             board[x][y] = '.';

51         }                

52 

53         // because all the possibility is impossiable.                

54         return false;

55     }

56     

57     public boolean isValid1(char[][] board, int x, int y) {

58         // Judge the column.

59         for (int i = 0; i < 9; i++) {

60             if (i != x && board[i][y] == board[x][y]) {

61                 return false;

62             }

63         }

64         

65         // Judge the row.

66         for (int i = 0; i < 9; i++) {

67             if (i != y && board[x][i] == board[x][y]) {

68                 return false;

69             }

70         }

71         

72         // Judge the block.

73         int i = x / 3 * 3;

74         int j = y / 3 * 3;

75         for (int k = 0; k < 9; k++) {

76             int xIndex = i + k / 3;

77             int yIndex = j + k % 3;

78             if (xIndex == x && yIndex == y) {

79                 continue;

80             }

81             

82             if (board[xIndex][yIndex] == board[x][y]) {

83                 return false;

84             }

85         }

86         

87         return true;

88     }
View Code

摘抄部分解释:

典型DFS/递归/回溯/深搜题。对于DFS,说白了

1) 什么时候返回?在本题中,

1.当x>8或y>8 表示已经遍历完所有的格子,因此成功完成,返回true。

2.当下一个搜索(子搜索)返回true,说明已经找到,返回true。  

3.如果测试过本轮的所有可能解,但无一是对的,说明无解,返回false。  

4.如果当前空格不是空格,则改变x,y坐标后,继续下一个空格的尝试

2)DFS就是针对本轮的所有可能解进行逐一尝试,找到本轮的一个可能解后,这时要调用递归,基于本轮的解对下一轮(子问题)进行求解。如果下一轮(子问题)求解成功,则说明大功告成,及时返回true,停止之后的尝试。

否则如果下一轮(子问题)求解失败,则说明本轮的解不适合子问题,因此,必须换一个本轮的解,然后基于本轮的新解,继续尝试子问题。如果已经本轮所有的解都尝试过了,也都失败了,说明本问题无解,返回false。

当然在每次尝试子问题前和如果失败返回后,都要恢复原来的环境(撤销动作)。

所以,要想使DFS成功返回,条件就是找到满足本轮的解和这个解也要满足下一轮(子问题)。

另外:

1 每个backtracking的题目,最好都有独立判断isValid的程序,这样架构清楚。同时,valid判断函数在这里可以稍微研究一下。只要当前要判断的位置上的数值和本行没有重复,本列没有重复,九宫格没有重复就可以。一旦重复立即返回,减少判断次数。

2 backtracking的递归函数,怎么能没有返回值呢?因为要判断递归的方案正确与否,所以这里的递归一定是有返回值的(除非是combination那种没有正确错误概念的backtracking)

3 可以考虑“先放置,再判断”的方案。比如这里,首先判断当前位置是否为空,如果为空,那么放置一个元素,检查它是否正确。如果正确,就继续进行下面的递归(也就是第29行 isValid&&solveSudoku的作用)。当函数返回错误之后,将刚刚的数值变为空,再进行下一次尝试即可。

4 所有的方案(k从1到9)完毕之后,应该返回错误,这个是不应该被忽略的。

2015.1.13 redo:

 1 public class Solution {  2     public void solveSudoku(char[][] board) {  3         // 3:01

 4         if (board == null || board.length == 0 || board[0].length == 0) {  5             return;  6  }  7         

 8         dfs(board, 0, 0);  9  } 10     

11     public boolean dfs(char[][] board, int x, int y) { 12         // 3:01 13         // next row.

14         if (y >= 9) { 15             return dfs(board, x + 1, 0); 16  } 17         

18         if (x >= 9) { 19             return true; 20  } 21         

22         // skip the number.

23         if (board[x][y] != '.') { 24             return dfs(board, x, y + 1); 25  } 26         

27         // solve the current node. 28         // BUG2: c start from 1 not 0.

29         for (char c = '1'; c <= '9'; c++) { 30             board[x][y] = c; 31             if (isValid(board, x, y, c) && dfs(board, x, y + 1)) { 32                 return true; 33  } 34             board[x][y] = '.'; 35  } 36         

37         return false; 38  } 39     

40     public boolean isValid(char[][] board, int x, int y, char c) { 41         // the current row. 

42         for (int i = 0; i < 9; i++) { 43             if (y != i && c == board[x][i]) { 44                 return false; 45  } 46  } 47         

48         // the current col. 

49         for (int i = 0; i < 9; i++) { 50             // BUG1: should use board[i][y]

51             if (x != i && c == board[i][y]) { 52                 return false; 53  } 54  } 55         

56         // the current block.

57         int startX = x / 3 * 3; 58         int startY = y / 3 * 3; 59         for (int k = 0; k < 9; k++) { 60             int indexX = startX + k / 3; 61             int indexY = startY + k % 3; 62             if (indexX == x && indexY == y) { 63                 continue; 64  } 65             

66             if (board[indexX][indexY] == c) { 67                 return false; 68  } 69  } 70         

71         return true; 72  } 73 }
View Code

 

GITHUB代码:

https://github.com/yuzhangcmu/LeetCode_algorithm/blob/master/hash/SolveSudoku.java

你可能感兴趣的:(LeetCode)