(30/60)重新安排行程、N皇后、解数独
重新安排行程
暂时没精力看,pass
N皇后
leetcode:51. N 皇后
回溯法
思路
遍历二维数组,每层递归遍历一层的可能性。
需要注意的是vector<string> 是非常自然的适合一层一层遍历的二维数组数据结构。
检查,如果满足要求,就放入Q。
复杂度分析
时间复杂度:
在 backtracking 函数中,我们会递归调用自身 backtracking(chessboard,n,row + 1),直到放置了 N 个皇后或者无法再放置皇后为止。因此,backtracking 函数的递归深度是 N,每一层的时间复杂度都是 O(N)。
综上所述,backtracking 函数的总时间复杂度可以表示为:O(N) * O(N) * ... * O(N) = O(N^N) = O(N!)。
空间复杂度:
使用一个二维数组来存储棋盘状态,因此空间复杂度为 O(N^2)。
注意点
-
终止条件是
row == n,行数等于n的时候。 -
c++严格区分字符(
'')和字符串("")。 -
二维数组:
[row][col]对应[i][j],行、列检查谁,谁不变。 -
vector<string>里放的是string。 -
由于是自上而下遍历:
- 不需检查行。
- 检查列只需检查到
0 ~ row-1。 - 检查撇、捺的时候需要自下而上检查(初始位置好找)
代码实现
class Solution {
private:
vector<vector<string>> result;
public:
// 二维遍历
void backtracking(vector<string>& chessboard,int n,int row){
if(row == n){
result.push_back(chessboard);
return;
}
for(int col = 0;col < n;col++){
if(isValid(chessboard,row,col,n)){
chessboard[row][col] = 'Q';
backtracking(chessboard,n,row + 1);
chessboard[row][col] = '.';
}
}
}
bool isValid(vector<string>& chessboard,int row,int col,int n){
// 不用检查横
// 检查竖
for(int i = 0;i < row;i++){ // col不变,row变
if(chessboard[i][col] == 'Q') return false;
}
// 检查撇(自下而上)
for(int i = row - 1,j = col + 1;i >= 0 && j < n;i--,j++){
if(chessboard[i][j] == 'Q') return false;
}
// 检查捺(自下而上)
for(int i = row - 1,j = col - 1;i >= 0 && j >= 0;i--,j--){
if(chessboard[i][j] == 'Q') return false;
}
return true;
}
vector<vector<string>> solveNQueens(int n) {
vector<string> chessboard(n,string(n,'.')); // 棋盘,二维数组(string就是天然一维数组)
backtracking(chessboard,n,0);
return result;
}
};
解数独
leetcode:37. 解数独
回溯法
思路
二维递归,前两层for循环寻找第一个空字符的行列坐标,然后第三层循环在当前位置遍历1-9看是否满足解的规则,如果满足,递归地查找下一个空字符,直到遍历完整个9*9数字盘都没有return,说明已经填满了数独,找到了解,返回true,由于之前设置了条件判断,会层层返回true,不再继续遍历;如果1-9遍历完后全不满足(没有返回true),那么说明当前这个解无法进行下去,return false回溯到上一个空字符循环1-9处。
复杂度分析
时间复杂度:
- 在
backtracking函数中,有三个嵌套的循环。外层两层循环遍历整个数独棋盘,因此时间复杂度为 O(N^2),其中 N = 9,即数独的大小为 9x9。 - 在内部的第三层循环中,会尝试填入数字 1 到 9,因此这个循环的时间复杂度是 O(1)。
- 在
isValid函数中,有三个嵌套的循环,分别用来检查所在行、列和九宫格内是否已经存在相同的数字。这三个循环的时间复杂度均为 O(9) = O(1),因为数独的大小是固定的。 - 综上所述,整个算法的时间复杂度取决于
backtracking函数,为 O(N^2) * O(1) = O(1)。
空间复杂度:O(1)。
注意点
-
检查九宫格的算法很神奇:
int startRow = (row/3)*3; int startCol = (col/3)*3; for(int i = startRow;i < startRow + 3;i++){ for(int j = startCol;j < startCol + 3;j++){ if(board[i][j] == k) return false; } }
代码实现
class Solution {
public:
// 二维递归
// 两层for定位行列位置
// 第三层for遍历尝试1-9
// 由于只需要一个结果,找到时直接层层返回
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;
// 找到解后会层层返回true
if(backtracking(board)) return true;
board[i][j] = '.';
}
}
// 如果1-9都遍历完了还没有返回true,
// 说明当前没有解
return false;
}
}
}
// 如果完整遍历完了board都没有return false
// 说明找到了解
return true;
}
bool isValid(vector<vector<char>>& board,int row,int col,char k){
// 检查行、列、所在九宫格
for(int i = 0;i < 9;i++){ // 检查行,变列
if(board[row][i] == k) return false;
}
for(int i = 0;i < 9;i++){ // 检查列,变行
if(board[i][col] == k) return false;
}
int startRow = (row/3)*3;
int startCol = (col/3)*3;
for(int i = startRow;i < startRow + 3;i++){
for(int j = startCol;j < startCol + 3;j++){
if(board[i][j] == k) return false;
}
}
return true;
}
void solveSudoku(vector<vector<char>>& board) {
backtracking(board);
}
};

浙公网安备 33010602011771号