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

posted on 2014-11-01 20:54  Yu's Garden  阅读(1621)  评论(0编辑  收藏  举报

导航