题目:
编写一个程序,通过填充空格来解决数独问题。
数独的解法需 遵循如下规则:
数字 1-9 在每一行只能出现一次。
数字 1-9 在每一列只能出现一次。
数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。(请参考示例图)
数独部分空格内已填入了数字,空白格用 '.' 表示。
示例 1:
输入:board = [["5","3",".",".","7",".",".",".","."],["6",".",".","1","9","5",".",".","."],[".","9","8",".",".",".",".","6","."],["8",".",".",".","6",".",".",".","3"],["4",".",".","8",".","3",".",".","1"],["7",".",".",".","2",".",".",".","6"],[".","6",".",".",".",".","2","8","."],[".",".",".","4","1","9",".",".","5"],[".",".",".",".","8",".",".","7","9"]]
输出:[["5","3","4","6","7","8","9","1","2"],["6","7","2","1","9","5","3","4","8"],["1","9","8","3","4","2","5","6","7"],["8","5","9","7","6","1","4","2","3"],["4","2","6","8","5","3","7","9","1"],["7","1","3","9","2","4","8","5","6"],["9","6","1","5","3","7","2","8","4"],["2","8","7","4","1","9","6","3","5"],["3","4","5","2","8","6","1","7","9"]]
解释:输入的数独如上图所示,唯一有效的解决方案如下所示:
提示:
board.length == 9
board[i].length == 9
board[i][j] 是一位数字或者 '.'
题目数据 保证 输入数独仅有一个解
思路:
这道题我们可以选用递归的方法来完成这道题,就是通过先假设一个位置填充一个数字,看之后会不会出现冲突,若出现冲突,则递归回退到出现问题的那个位置,反之则完成题目
代码:
class Solution {
public:
bool line[9][9], column[9][9], box[3][3][9];
//line 记录某行,某位数字是否已经被摆放
//column 记录某列,某位数字是否已经被摆放
//box 记录某3*3宫格内,某位数字是否被摆放
bool dfs (vector<vector<char>>& board, int row, int col)
{
//搜索空位
while(board[row][col] != '.'){
if (++col >= 9) { //若大于9则说明这一行已经结束
row++;
col = 0;
}
if (row >= 9) { //若大于9则说明这9行都已经结束了 直接返回即可
return true;
}
}
for (int i = 0; i < 9; i++)//填充1-9
{
if (line[row][i] == false && column[col][i] == false && box[row / 3][col / 3][i] == false)
//确保这一行,一列,以及在这个3*3宫格内i这个数字没有被填充过
{
line[row][i] = true; //这一行已经输入了i
column[col][i] = true; //这一列已经输入了i
box[row / 3][col / 3][i] = true; //3*3宫格内已经填充了i
board[row][col] = i + '0' + 1; //填充到board
if(dfs(board, row, col)){ //开始递归 若返回值为真,则说明填充成立 反之填充有误
return true;
}
else{ //开始递归回退 将状态复原到之前
line[row][i] = false;
column[col][i] = false;
box[row / 3][col / 3][i] = false;
board[row][col] = '.';
}
}
}
return false; //此时说明这种填充有误,需要递归回退
}
void solveSudoku(vector<vector<char>>& board) {
for (int i = 0; i < 9; i++)
{
for (int j = 0; j < 9; j++)
{
if (board[i][j] != '.') //对哪个位置有数进行记录
{
int temp = board[i][j] - '0' - 1;
line[i][temp] = true;
column[j][temp] = true;
box[i / 3][j / 3][temp] = true;
}
}
}
dfs(board, 0, 0);
}
};
讲解:
bool line[9][9], column[9][9], box[3][3][9];
//line 记录某行,某位数字是否已经被摆放
//column 记录某列,某位数字是否已经被摆放
//box 记录某3*3宫格内,某位数字是否被摆放
这里是我们需要的一些标志位,我在里面已经写下了他们对应的作用
void solveSudoku(vector<vector<char>>& board) {
for (int i = 0; i < 9; i++)
{
for (int j = 0; j < 9; j++)
{
if (board[i][j] != '.') //对哪个位置有数进行记录
{
int temp = board[i][j] - '0' - 1;
line[i][temp] = true;
column[j][temp] = true;
box[i / 3][j / 3][temp] = true;
}
}
}
dfs(board, 0, 0);
}
这里是这段代码的主函数,程序运行到这个函数的时候会将现在棋盘已经输入过数字的位置通过咱们刚才设置的标志位进行标记,标记结束之后,进入这段代码最重点的部分dfs函数
bool dfs (vector<vector<char>>& board, int row, int col)
{
//搜索空位
while(board[row][col] != '.'){
if (++col >= 9) { //若大于9则说明这一行已经结束
row++;
col = 0;
}
if (row >= 9) { //若大于9则说明这9行都已经结束了 直接返回即可
return true;
}
}
for (int i = 0; i < 9; i++)//填充1-9
{
if (line[row][i] == false && column[col][i] == false && box[row / 3][col / 3][i] == false)
//确保这一行,一列,以及在这个3*3宫格内i这个数字没有被填充过
{
line[row][i] = true; //这一行已经输入了i
column[col][i] = true; //这一列已经输入了i
box[row / 3][col / 3][i] = true; //3*3宫格内已经填充了i
board[row][col] = i + '0' + 1; //填充到board
if(dfs(board, row, col)){ //开始递归 若返回值为真,则说明填充成立 反之填充有误
return true;
}
else{ //开始递归回退 将状态复原到之前
line[row][i] = false;
column[col][i] = false;
box[row / 3][col / 3][i] = false;
board[row][col] = '.';
}
}
}
return false; //此时说明这种填充有误,需要递归回退
}
首先函数最开始的while循环是为了寻找空位,当找到一个空位时,我们再往下去进行下面的操作,在这里我们也对行(row)和列(col)进行了维护。
接下来的for循环就是为了去填充1-9的数字
if (line[row][i] == false && column[col][i] == false && box[row / 3][col / 3][i] == false)
//确保这一行,一列,以及在这个3*3宫格内i这个数字没有被填充过
这个if起到判断是否需要进行填充的作用
line[row][i] = true; //这一行已经输入了i
column[col][i] = true; //这一列已经输入了i
box[row / 3][col / 3][i] = true; //3*3宫格内已经填充了i
board[row][col] = i + '0' + 1; //填充到board
if(dfs(board, row, col)){ //开始递归 若返回值为真,则说明填充成立 反之填充有误
return true;
}
else{ //开始递归回退 将状态复原到之前
line[row][i] = false;
column[col][i] = false;
box[row / 3][col / 3][i] = false;
board[row][col] = '.';
}
上面那四行就是表明了我们在思路当中所说的假设这个位置为这个数字,紧接着后面的if是来去进行递归的,看下层递归的返回值是否为假,为假则说明这一步或前面几步填充有误,所以我们需要进行下面的递归回退,让棋盘状态回归到出错的地方,让出错的那层循环的for循环+1,去假设填充下一个数字,直到最总完成棋盘的填充。