原题地址(困难)
思路
好久没做困难难度的题目了。
但是这道题,倒不是说多难想,而是说过程复杂了一点,代码多了一点,思路还是很简单的。
就是回溯。
- 我们遍历9*9网格中的每一个元素,如果已经有数了,便略过;
- 如果没数,则这个格子可以有
0~9
十种选择。 - 对于每一种选择,我们判断是否满足行、列、3*3方格内都不重复这三个条件。
- 如果不满足,则接着尝试下一个数字。
- 如果满足,则继续处理下一个格子。
回溯体现在什么地方呢?
就是某个格子(记为格子 x
)填了一个数(记为 num
),满足行、列、3*3方格这三个条件了,然后继续填后面的格子。然而后面的格子怎么填,都不能解出数独。说明格子 x
不能填入数 num
。所以这时候就需要回溯到格子 x
,继续尝试其他的数。
**
核心还是在回溯,只不过判断条件啥的稍微麻烦了一些。套上回溯的模板,捋顺思路慢慢写,还是很好写的。
回溯模板
void dfs(){
//终止条件
....
// 递归
for(int i=1; i<=9; i++){
如果填入数字i满足不重复条件{
填入数字i
dfs() // 继续递归
去除数字i
}
}
}
详见代码,带有详细注释
cpp代码
class Solution {
public:
/* row[i][j] = 1 第i行已经存在数字j
column[i][j] = 1 第i列已经存在数字j
square[i][j][k] = 1 共有9个小方格 , 第[i][j]个小方格已经存在数字k
*/
int row[9][10], column[9][10], square[3][3][10];
vector<vector<char>> board;
// dfs()函数含义:从board[i,j]这个格子开始解,如果可以解出数独,就返回true,否则返回false
bool dfs(int i, int j, int right, int bottom){
// 因为递归board是按行递归,所以最后的边界就是 行越界
if(i > bottom ) return true;
// 如果该格子还没有元素
if(board[i][j] == '.'){
for(int k =1; k<=9; k++){
// if条件里是三个判断不重复的条件,如果满足,就把这个数字放进格子
if(!row[i][k] && !column[j][k] && !square[i/3][j/3][k]){
board[i][j] = '0' + k; //把数字放进格子
row[i][k] = column[j][k] = square[i/3][j/3][k] = 1; // 相应的标记数组更新
// 继续进行下一个格子
if(j < right && dfs(i, j+1, right, bottom))
return true;
else if(j == right && dfs(i+1, 0, right, bottom))
return true;
// 恢复原状,目的是为了进行下一个数字的尝试
board[i][j] = '.';
row[i][k] = column[j][k] = square[i/3][j/3][k] = 0;
}
}
return false;
}
else{ // 该格子已经有元素了,略过,直接处理下一个格子
if(j < right && dfs(i, j+1, right, bottom))
return true;
else if(j == right && dfs(i+1, 0, right, bottom))
return true;
else return false;
}
}
void solveSudoku(vector<vector<char>>& board) {
memset(row, 0, sizeof(row));
memset(column, 0, sizeof(column));
memset(square, 0, sizeof(square));
for(int i=0; i<9; i++){
for(int j=0; j<9; j++){
char c = board[i][j];
if(c != '.')
row[i][c-'0'] = column[j][c-'0'] = square[i/3][j/3][c-'0'] = 1;
}
}
this->board = board;
bool b = dfs(0, 0, 8, 8);
board = this->board;
return;
}
};