题目1:332 重新安排形成
题目链接:重新安排行程
对题目的理解
题目2:51 N皇后
题目链接:N皇后
对题目的理解
将皇后放置在不同行,不同列,不同对角线
!!!注意检查不同列时,一定要是纵向检查,不能是横向检查,因为递归的时候,一定是新的一行进行,这新的一行肯定是没有皇后的,那么这么横向比较毫无意义,所以进行纵向比较,判断这一列有没有皇后。
回溯法
回溯三部曲
i)递归函数参数
定义全局变量二维数组result来记录最终结果。
参数n是棋盘的大小,然后用row来记录当前遍历到棋盘的第几层了。
ii)递归终止条件
当递归到棋盘最底层(也就是叶子节点)的时候,就可以收集结果并返回了。
iii)单层搜索的逻辑
递归深度就是row控制棋盘的行,每一层里for循环的i控制棋盘的列,一行一列,确定了放置皇后的位置。
每次都是要从新的一行的起始位置开始搜,所以都是从0开始。
代码
class Solution {
public:
vector<vector<string>> result;
void backtracking(vector<string>& cheeseboard,int n,int row){
//终止条件
if(row==n){
result.push_back(cheeseboard);
return;
}
//单层搜索逻辑
for(int i=0;i<n;i++){//列
if(isvalid(row,i,cheeseboard,n)){
cheeseboard[row][i] = 'Q';
backtracking(cheeseboard,n,row+1);//递归,因为要向下一行递归
cheeseboard[row][i] = '.';//回溯
}
}
}
bool isvalid(int row,int col,vector<string>& cheeseboard,int n){
//检查每一行的同一列有没有,只有这样检查才是争取的检查
//不能检查同一行的每一列,这样每一比较意义,因为每一行都是新的一行,都没有元素
for(int i=0;i<row;i++)
if(cheeseboard[i][col]=='Q') return false;
//检查45度角
for(int i=row-1,j=col-1;i>=0 && j>=0;i--,j--)
if(cheeseboard[i][j]=='Q') return false;
//检查135度角
for(int i=row-1,j=col+1;i>=0 && j<n;i--,j++)
if(cheeseboard[i][j]=='Q') return false;
return true;
}
vector<vector<string>> solveNQueens(int n) {
result.clear();
std::vector<std::string> cheeseboard(n,std::string(n,'.'));
backtracking(cheeseboard,n,0);
return result;
}
};
- 时间复杂度: O(n!)
- 空间复杂度: O(n)
题目3: 37 解数独
题目链接:解数独
对题目的理解
1-9在每一行出现一次,每一列出现一次,在3*3格内出现一次
注意本题与N皇后的区别
N皇后是每一行每一列只放一个皇后,需要一层for循环遍历一行,递归来遍历列,然后一行一列确定皇后的唯一位置;
本题是棋盘的每一个位置都要放置一个数字,所以树形结构要比N皇后更宽更深
回溯法
回溯三部曲
i)递归函数以及参数
递归函数的返回值需要是bool类型,因为解数独找到一个符合的条件(就在树的叶子节点上)立刻就返回,相当于找从根节点到叶子节点一条唯一路径,所以需要使用bool返回值
ii)递归终止条件
本题递归不用终止条件,解数独是要遍历整个树形结构寻找可能的叶子节点就立刻返回。
不用终止条件会不会死循环?
递归的下一层的棋盘一定比上一层的棋盘多一个数,等数填满了棋盘自然就终止(填满当然好了,说明找到结果了),所以不需要终止条件!
iii)递归单层搜索逻辑
一个for循环遍历棋盘的行,一个for循环遍历棋盘的列,一行一列确定下来之后,递归遍历这个位置放9个数字的可能性!
判断棋盘是否合法
同行是否重复,同列是否重复,9宫格里是否重复
代码
class Solution {
public:
bool backtracking(vector<vector<char>>& board){
//单层搜索逻辑,二维递归
for(int i=0;i<9;i++){
for(int j=0;j<9;j++){
if(board[i][j]=='.'){//遇到空格
for(char k='1';k<='9';k++){
if(isvalid(board,i,j,k)){
board[i][j]=k;
// return true;
bool result = backtracking(board);
if(result==true) return true;
board[i][j] = '.'; //回溯
}
}
return false;//9个数都尝试了,都不行
}
}
}
return true;//空格都被填满了,没有返回false说明找到了一个棋盘
}
bool isvalid(vector<vector<char>>& board,int row,int col,char val){
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;//除以3是看这一行位于第几个九宫格内,再乘以3是看位于九宫格内的第几行,这个就是求解元素所在九宫格的第一个行
int startcol = (col/3)*3;//求解元素所在九宫格的第一列
for(int i=startrow;i<startrow+3;i++){//判断9宫格内的元素是否重复,startrow+3代表九宫格的最后一行
for(int j=startcol;j<startcol+3;j++){//startcol+3代表九宫格的最后一列
if(board[i][j]==val) return false;
}
}
return true;
}
void solveSudoku(vector<vector<char>>& board) {
//注意这是一个void类型
backtracking(board);
}
};