(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)。

注意点

  1. 终止条件是row == n,行数等于n的时候。

  2. c++严格区分字符('')和字符串("")。

  3. 二维数组:

    [row][col]对应[i][j],行、列检查谁,谁不变。

  4. vector<string>里放的是string

  5. 由于是自上而下遍历:

    1. 不需检查行。
    2. 检查列只需检查到0 ~ row-1
    3. 检查撇、捺的时候需要自下而上检查(初始位置好找)

代码实现

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)。

注意点

  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);
    }
};
posted @ 2024-02-28 00:16  Tazdingo  阅读(918)  评论(0)    收藏  举报