1.题目链接:
37. 解数独
2.解题思路:
2.1.题目要求:
暂时的理解就是,编写一个程序然后自动填完数独,填完返回(不用求解各种不同的数独组合)
填的时候,数字要满足的规则:
- 给定的数独序列只包含数字 1-9 和字符 ' . '(空格) 。
- 你可以假设给定的数独只有唯一解。
- 给定数独永远是 9x9 形式的。
# 思路
2.2.思路:
大概思路就是双层 for 一层横一层竖,然后遍历 1 - 9 满足条件就下一层,直到填满,填满了直接返回,卡住或者没填满不符合条件就返回false。
(这里定义的是布尔类型,可以只填满一次就返回)
2.3.回溯三部曲:
2.3.1.确定回溯函数参数
因为解数独找到一个符合的条件(就在上面N叉树的叶子节点上)立刻就返回,相当于找从根节点到叶子节点一条唯一路径,所以需要使用bool返回值。
代码如下:
bool backtracking(vector<vector<char>>& board)
2.3.2.确定终止条件
没有,填满棋盘自然终止,但是会有遇见一个空1-9都填不进去的情况,需要向上递归,查找下一个解,也就是返回false,在单层遍历上有解释。
2.3.3.确定单层遍历逻辑
双for遍历整个棋盘,跳过非空格的格子,然后在能遍历的格子上,尝试 '1' - '9' (字符串) 合不合适,合适就放并继续向下递归去填 数字 ,这里会有个东西接着递归的判断,判断为 true ,就会一层层向上返回,这种返回有三种情况,第一种是棋盘被填满的返回,那这样,就不会触发下面回溯的逻辑,直接向上一层层结束。第二种是没填满,这一格填1-9都不合适,那这样for(1-9)就遍历完结束了,然后进行回溯的操作,继续遍历来填1-9。第三种就是1-9都不合适在向下遍历的过程中,那这样,就会慢慢的一层层向上回溯,自然结束整个函数
bool backtracking(vector<vector<char>>& board) {
for (int i = 0; i < board.size(); i++) { // 遍历行
for (int j = 0; j < board[0].size(); j++) { // 遍历列
if (board[i][j] != '.') continue;
for (char k = '1'; k <= '9'; k++) { // (i, j) 这个位置放k是否合适
if (isValid(i, j, k, board)) {
board[i][j] = k; // 放置k
if (backtracking(board)) return true; // 如果找到合适一组立刻返回
board[i][j] = '.'; // 回溯,撤销k
}
}
return false; // 9个数都试完了,都不行,那么就返回false
}
}
return true; // 遍历完没有返回false,说明找到了合适棋盘位置了
}
2.3.4判断棋盘是否合法
判断棋盘是否合法有如下三个维度:
- 同行是否重复
- 同列是否重复
- 9宫格里是否重复
代码如下:
bool isValid(int row, int col, char val, vector<vector<char>>& board) {
for (int i = 0; i < 9; i++) { // 判断行里是否重复
if (board[row][i] == val) {
return false;
}
}
for (int j = 0; j < 9; j++) { // 判断列里是否重复
if (board[j][col] == val) {
return false;
}
}
int startRow = (row / 3) * 3;
int startCol = (col / 3) * 3;
for (int i = startRow; i < startRow + 3; i++) { // 判断9方格里是否重复
for (int j = startCol; j < startCol + 3; j++) {
if (board[i][j] == val ) {
return false;
}
}
}
return true;
}
2.4.总代码:
class Solution {
private:
bool backtracking(vector<vector<char>>& board) {
for (int i = 0; i < board.size(); i++) { // 遍历行
for (int j = 0; j < board[0].size(); j++) { // 遍历列
if (board[i][j] == '.') {
for (char k = '1'; k <= '9'; k++) { // (i, j) 这个位置放k是否合适
if (isValid(i, j, k, board)) {
board[i][j] = k; // 放置k
if (backtracking(board)) return true; // 如果找到合适一组立刻返回
board[i][j] = '.'; // 回溯,撤销k
}
}
return false; // 9个数都试完了,都不行,那么就返回false
}
}
}
return true; // 遍历完没有返回false,说明找到了合适棋盘位置了
}
bool isValid(int row, int col, char val, vector<vector<char>>& board) {
for (int i = 0; i < 9; i++) { // 判断行里是否重复
if (board[row][i] == val) {
return false;
}
}
for (int j = 0; j < 9; j++) { // 判断列里是否重复
if (board[j][col] == val) {
return false;
}
}
int startRow = (row / 3) * 3;
int startCol = (col / 3) * 3;
for (int i = startRow; i < startRow + 3; i++) { // 判断9方格里是否重复
for (int j = startCol; j < startCol + 3; j++) {
if (board[i][j] == val ) {
return false;
}
}
}
return true;
}
public:
void solveSudoku(vector<vector<char>>& board) {
backtracking(board);
}
};
3.遇见的问题:
1.填满的返回过程?
(主要是{}的分界没好好的区分开造成的混乱)
没事了,for里面的for的return...
2. 返回ture和false又没东西接着,干啥用的?
哦哦,遇见ture会终止当前循环。
3.
4.记录:
12.05
有点难才有意思嘛,不过做不出来也很合理对吧,
看代码,一层层层层层层层层层层层层的,头大...
12.12
欸,N皇后和解数独都没写代码,算了,赶快过一遍,假期进度刷起来。