37. 解数独(困难)
题目描述:
编写一个程序,通过填充空格来解决数独问题。
数独的解法需 遵循如下规则:
数字 1-9 在每一行只能出现一次。
数字 1-9 在每一列只能出现一次。
数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。(请参考示例图)
数独部分空格内已填入了数字,空白格用 ‘.’ 表示。
考察重点:递归回溯(DFS)
方法概括:通过36中的判别方法,构建row,column,tube进行合法性判定,结合深度优先搜索对矩阵中的 ‘.’ 点位遍历所有可能填入数字。
public boolean Dfs(int i, int j, char[][] board, boolean[][] row, boolean[][] column, boolean[][] tube){ //后三个参数分别用来判断,当前位置(i,j)填入一数字后是否合法int p = i / 3 * 3 + j / 3;for(int a1 = 0;a1 < 9 ;a1 ++){ //遍历1-9,即该位置可以填入的所有数字if(!row[i][a1] && !column[j][a1] && !tube[p][a1]){//判断该位置填入相应数字后,是否负责数独规则row[i][a1] = true;column[j][a1] = true;tube[i / 3 * 3 + j / 3][a1] = true;board[i][j] = (char)(a1 + 1 + '0');int tj = j + 1;if (i == 8 && j == 8)return true;for(int ti = i;ti < 9;ti ++) { //当前位置填入a1+1后,继续寻找并迭代下一个'.'位置for (; tj < 9; tj++) {if (board[ti][tj] == '.') {if (Dfs(ti, tj, board, row, column, tube))return true;ti = 10;break;}if (ti == 8 && tj == 8) //两处if (ti == 8 && tj == 8)分别用来做递归停止条件,即分别为board[8][8]是'.'和数字的情况return true;}tj = 0; //因为遍历是从(i,j)开始找下一个,所以此处tj=0是配合循环实现矩阵遍历}row[i][a1] = false; //填入a1无法满足后续位置满足条件,则向前回溯column[j][a1] = false;tube[i / 3 * 3 + j / 3][a1] = false;board[i][j] = '.';}}return false;}
public void solveSudoku(char[][] board) {boolean[][] row = new boolean[9][9]; // 9个长度为9的判别串。分别用来判断(i,j)上可以存放的数字,row[i][a1]=true 表示第i行已经存在a1boolean[][] column = new boolean[9][9]; //column[j][a1]=true 表示第j列已经存在a1boolean[][] tube = new boolean[9][9];//tube[i / 3 * 3 + j / 3][a1]=true 表示第i / 3 * 3 + j / 3个正方形内已经存在a1for(int i = 0;i < board.length;i ++)for(int j = 0;j < board[0].length;j ++){ //由于已经拥有一些数字,所以进行初始化if(board[i][j] != '.'){int a1 = board[i][j] - '0' - 1;row[i][a1] = true;column[j][a1] = true;tube[i / 3 * 3 + j / 3][a1] = true;}}for(int i = 0;i < board.length;i ++) //寻找迭代起始位置,即第一个'.'for(int j = 0;j < board[0].length;j ++)if(board[i][j] == '.')Dfs(i, j, board, row, column, tube);
}