持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第19天,点击查看活动详情
今天,我们继续搞算法。
题目描述
编写一个程序,通过填充空格来解决数独问题。
数独的解法需 遵循如下规则:
数字 1-9 在每一行只能出现一次。 数字 1-9 在每一列只能出现一次。 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。(请参考示例图) 数独部分空格内已填入了数字,空白格用 '.' 表示。
题目分析
这个题目是要我们解数独,我第一次看到这么多密密麻麻的数字我也是一头雾水,感觉应该是道困难题,哈哈,正如大家所见,但是我们不能北困难吓倒,因为我们已经有了一个验证数独的代码,所以,我们怎么样,把这个代码作为数独的验证,然后我们枚举一个数填进去不就好了嘛,本着让大家理解的方式,本题采用模块话的方式帮助大家解决这个题。
那我们的思路就很清晰了,第一步找到一个空,然后枚举判断是否有效,然后还原现场。为啥要还原现场呢?因为你枚举不一定是对的呀,如果是ture那就返回ture,如果是fasle呢?还得把原来的改回来,再接着枚举,说干就干。
之后呢,就干超时了
为啥超时呢?因为你可能性太多了,第一个枚举1-9,最后是9的81次方,所以我们干什么呢?剪枝。
我们这样来考虑,不是搜索空位置,而是搜索最小可能性,枚举i,j,如果是空,我们就看看这个位置能填什么数,我们选一个最小的位置填入,先把这个位置返回填入,然后不是枚举1-9,是枚举这个位置可能的数字,如果填了,我们就做个行列块标记,如果填之后不满足,那我们就需要回溯,重新填写,直到填完所有位置。
最终结果:
解题思路
- 确定操作对象:本题中,操作对象1个root,1个board
- 确定操作条件:这个题操作条件很多,关键是看这个位置有多少数能填,返回一个最小的位置进行填写,之后剩下的分支就会少。
- 确定操作过程:操作过程就是枚举可能填入的数字,依次填入。
- 确定结果返回:返回最终结果。
代码
class Solution {
private Boolean[][] rowFlag = new Boolean[9][10];
private Boolean[][] colFlag= new Boolean[9][10];
private Boolean[][] boxFlag = new Boolean[9][10];
public void solveSudoku(char[][] board) {
int m = board.length;
for(int i =0;i<9;i++){
for(int digit=1;digit<=9;digit++){
rowFlag[i][digit]=true;
colFlag[i][digit]=true;
boxFlag[i][digit] =true;
}
}
for(int i=0;i<9;i++){
for(int j=0;j<9;j++){
int ch = board[i][j];
if(ch == '.') continue;
int index = ch - '0';
rowFlag[i][index]=false;
colFlag[j][index]=false;
boxFlag[boxIndex(i,j)][index] =false;
}
}
dfs(board);
}
public int boxIndex(int i ,int j){
return (i/3)*3+(j/3);
}
public Boolean dfs(char[][] board){
// if(!isValidSudoku(board)) return false;
int[] arr = searchMiniPossablity(board);
int x = arr[0];
int y = arr[1];
if(x == -1) return true;
List<Integer> digits = getAvailableDigits(x,y);
for(Integer digit: digits){
board[x][y]=(char)('0'+digit.intValue());
rowFlag[x][digit]=false;
colFlag[y][digit]=false;
boxFlag[boxIndex(x,y)][digit] =false;
if(dfs(board)) return true;
board[x][y]='.';
rowFlag[x][digit]=true;
colFlag[y][digit]=true;
boxFlag[boxIndex(x,y)][digit] =true;
}
return false;
}
public List<Integer> getAvailableDigits(int i, int j){
List<Integer> getAvailableDigits = new ArrayList<>();
for(int digit=1;digit<=9;digit++){
if(rowFlag[i][digit] && colFlag[j][digit] && boxFlag[boxIndex(i,j)][digit]){
getAvailableDigits.add(digit);
}
}
return getAvailableDigits;
}
public int[] searchMiniPossablity(char[][] board){
int miniValue = 10;
int[] arr = new int[2];
arr[0] = -1;
arr[1] = -1;
int m = board.length;
int n = board[0].length;
for(int i =0;i<m;i++){
for(int j=0;j<n;j++){
if(board[i][j] != '.') continue;
List list = getAvailableDigits(i,j);
if(list.size()<= miniValue){
miniValue = list.size();
arr[0] = i;
arr[1]= j;
}
}
}
return arr;
}
}