【leetcode刷题记录二】

三、算法技巧

3.1 暴力搜索

3.1.1 回溯算法解题套路框架

框架

result = []
def backtrack(路径, 选择列表):
    if 满足结束条件:
        result.add(路径)
        return
    
    for 选择 in 选择列表:
        做选择
        backtrack(路径, 选择列表)
        撤销选择

1、全排列 leetcode

题解:
时间复杂度:O(n x n!)
空间复杂度:O(n)

class Solution {
public:
    vector<vector<int> > ans;
    vector<int> vis;
    void backtrace(vector<int>& nums, vector<int> path)
    {
        if(path.size()==nums.size())
        {
            ans.push_back(path);
            return;
        }
        for(int i=0;i<nums.size();i++)
        {
            if(vis[i]==0)
            {
                vis[i]=1;
                path.push_back(nums[i]);
                backtrace(nums, path);
                path.pop_back();
                vis[i]=0;
            }
        }
    }
    vector<vector<int>> permute(vector<int>& nums) {
        vis  = vector<int>(nums.size(), 0);
        backtrace(nums, {});
        return ans;
    }
};

2、N 皇后 leetcode

题解:
时间复杂度:O(n!)
空间复杂度:O(n)

class Solution {
public:
    vector<vector<string> > ans;
    bool judge(vector<string>& grid, int i, int j, int n)
    {
        for(int q=0;q<n;q++)
        {
            if(grid[i][q] == 'Q')
            {
                return false;
            }
        }
        for(int p=0;p<n;p++)
        {
            if(grid[p][j] == 'Q')
            {
                return false;
            }
        }
        int new_i = i;
        int new_j = j;
        while(new_i>=0 && new_j>=0)
        {
            if(grid[new_i][new_j]=='Q')
            {
                return false;
            }
            new_i--;
            new_j--;
        }

        new_i = i;
        new_j = j;
        while(new_i<n && new_j<n)
        {
            if(grid[new_i][new_j]=='Q')
            {
                return false;
            }
            new_i++;
            new_j++;
        }

        new_i = i;
        new_j = j;
        while(new_i>=0 && new_j<n)
        {
            if(grid[new_i][new_j]=='Q')
            {
                return false;
            }
            new_i--;
            new_j++;
        }

        new_i = i;
        new_j = j;
        while(new_i<n && new_j>=0)
        {
            if(grid[new_i][new_j]=='Q')
            {
                return false;
            }
            new_i++;
            new_j--;
        }
        return true;
    }
    void backtrace(vector<string>& grid, int index, int n)
    {
        if(index==n)
        {
            ans.push_back(grid);
            return;
        }
        for(int i=0;i<n;i++)
        {
            if(judge(grid, index, i, n))
            {
                grid[index][i] = 'Q';
                backtrace(grid, index+1, n);
                grid[index][i] = '.';
            }
        }
    }
    vector<vector<string>> solveNQueens(int n) {
        vector<string> grid(n);
        string kong = "";
        for(int i=0;i<n;i++)
        {
            kong+='.';
        }
        for(int i=0;i<n;i++)
        {
            grid[i]=kong;
        }
        backtrace(grid, 0, n);
        return ans;
    }
};

3.1.2 集合划分问题

1、划分为k个相等的子集 leetcode

题解:
时间复杂度:O(k^n)
空间复杂度:O(k)

class Solution {
public:
    bool canPartitionKSubsets(vector<int>& nums, int k) {
        if(k > nums.size()) return false;
        int sum = accumulate(nums.begin(), nums.end(), 0);
        if(sum % k != 0) return false;

        // 记录每个子集中数字之和
        bucket.resize(k); 
        // 每个桶中的数字之和应该为target
        int target = sum/k;
        // 从大到小放数字
        sort(nums.rbegin(), nums.rend());
        // index = 0, 表示从0号元素开始遍历
        return backtrack(nums, 0, target);
    }

private:
    // 记录每个子集中数字之和
    vector<int> bucket;

    bool backtrack(vector<int> &nums, int index, int target){
        // 如果所有数字遍历完了,是不需要检查bucket中的元素和是否都是target的。因为前面的 if(sum % k != 0) return false; 已经能保证只要所有元素都放入bucket中,那么bucket中的元素和都为target。
        if(index == nums.size()){
            return true;
        }

        // 注意:i 表示第i个子集,index 表示第index个数字
        for(int i = 0; i < bucket.size(); i++){
            // 如果这个数字放入子集i中使子集i中元素和超出target了
            if(bucket[i] + nums[index] > target){
                continue;
            }
            // 如果 当前子集的元素和 与 前一个子集的元素和 是一样的,那就跳过
            if(i > 0 && bucket[i] == bucket[i-1]){
                continue;
            }
            // 将数字放入子集i中
            bucket[i] += nums[index];
            // 递归穷举下一个数字的情况
            if(backtrack(nums, index + 1, target)){
                return true;
            }
            // 撤销选择
            bucket[i] -= nums[index]; 
        }
        // 如果 nums[index] 放入哪个子集都不行
        return false;
    }
};

3.1.3 排列/组合/子集问题

1、子集 leetcode
(元素无重不可复选)

题解:
时间复杂度:O(n*2^n)
空间复杂度:O(n)

class Solution {
public:
    vector<vector<int> > ans;
    void backtrace(vector<int>& nums, int idx, vector<int> path)
    {
        ans.push_back(path);
        for(int i=idx;i<nums.size();i++)
        {
            path.push_back(nums[i]);
            backtrace(nums, i+1, path);
            path.pop_back();
        }
    }
    vector<vector<int>> subsets(vector<int>& nums) {
        backtrace(nums, 0, {});
        return ans;
    }
};

2、组合 leetcode
(元素无重不可复选)

题解:转化为子集问题
时间复杂度:O( C n k C_{n}^k Cnk×k)
空间复杂度:O(n)

class Solution {
public:
    vector<vector<int> > ans;
    vector<int> nums;
    void backtrace(int idx, int k, vector<int> path)
    {
        if(path.size()==k)
        {
            ans.push_back(path);
            return;
        }
        for(int i=idx;i<nums.size();i++)
        {
            path.push_back(nums[i]);
            backtrace(i+1, k, path);
            path.pop_back();
        }
    }
    vector<vector<int>> combine(int n, int k) {
        for(int i=1;i<=n;i++)
        {
            nums.push_back(i);
        }
        backtrace(0, k, {});
        return ans;
    }
};

题解:提前剪枝
时间复杂度:O( C n k C_{n}^k Cnk×k)
空间复杂度:O(n)

class Solution {
public:
    vector<vector<int> > ans;
    vector<int> nums;
    void backtrace(int cur, int k, vector<int> path, int n)
    {
        if(path.size() + (n - cur) < k)
        {
            return;
        }
        if(path.size()==k)
        {
            ans.push_back(path);
            return;
        }
        path.push_back(nums[cur]);
        backtrace(cur+1, k, path, n);
        path.pop_back();
        backtrace(cur+1, k, path, n);
    }
    vector<vector<int>> combine(int n, int k) {
        for(int i=1;i<=n;i++)
        {
            nums.push_back(i);
        }
        backtrace(0, k, {}, n);
        return ans;
    }
};

3、全排列 leetcode
(元素无重不可复选)

题解:
时间复杂度:O(n x n!)
空间复杂度:O(n)

class Solution {
public:
    vector<vector<int> > ans;
    vector<int> vis;
    void backtrace(vector<int>& nums, vector<int> path)
    {
        if(path.size()==nums.size())
        {
            ans.push_back(path);
            return;
        }
        for(int i=0;i<nums.size();i++)
        {
            if(vis[i]==0)
            {
                vis[i]=1;
                path.push_back(nums[i]);
                backtrace(nums, path);
                path.pop_back();
                vis[i]=0;
            }
        }
    }
    vector<vector<int>> permute(vector<int>& nums) {
        int n = nums.size();
        vis.resize(n, 0);
        backtrace(nums, {});
        return ans;
    }
};

4、子集 II leetcode
(元素可重不可复选)

题解:
时间复杂度:O(n x 2^n)
空间复杂度:O(n)

class Solution {
public:
    vector<vector<int> > ans;
    void backtrace(vector<int>& nums, int idx, vector<int> path)
    {
        ans.push_back(path);
        for(int i=idx;i<nums.size();i++)
        {
            if(i>idx && nums[i]==nums[i-1])
                continue;
            path.push_back(nums[i]);
            backtrace(nums, i+1, path);
            path.pop_back();
        }
    }
    vector<vector<int>> subsetsWithDup(vector<int>& nums) {
        sort(nums.begin(), nums.end());
        backtrace(nums, 0, {});
        return ans;
    }
};

5、组合总和 II leetcode
题解:
时间复杂度:O(n x 2^n)
空间复杂度:O(n)

class Solution {
public:
    vector<vector<int> > ans;
    void backtrace(vector<int>& candidates, int idx, int sum, int target, vector<int> path)
    {
        if(sum > target)
        {
            return;
        }
        if(sum==target)
        {
            ans.push_back(path);
        }
        for(int i=idx;i<candidates.size();i++)
        {
            if(i>idx && candidates[i]==candidates[i-1])
                continue;
            path.push_back(candidates[i]);
            backtrace(candidates, i+1, sum+candidates[i], target, path);
            path.pop_back();
        }
    }
    vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
        sort(candidates.begin(), candidates.end());
        backtrace(candidates, 0, 0, target, {});
        return ans;
    }
};

6、全排列 II leetcode

题解:
时间复杂度:O(n x n!)
空间复杂度:O(n)

class Solution {
public:
    vector<vector<int>> ans;
    vector<int> vis;
    void backtrace(vector<int>& nums, vector<int> path)
    {
        if(path.size()==nums.size())
        {
            ans.push_back(path);
            return;
        }
        for(int i=0;i<nums.size();i++)
        {
            if(vis[i]==0)
            {
            	// 新添加的剪枝逻辑,固定相同的元素在排列中的相对位置
                if(i>0 && nums[i]==nums[i-1] && vis[i-1]==0)
                	// 如果前面的相邻相等元素没有用过,那么当前元素也不能用,要跳过。只有当前面相等的元素用过了,当前相等元素才能用,这样保持了相对顺序。
                    continue;
                vis[i]=1;
                path.push_back(nums[i]);
                backtrace(nums, path);
                path.pop_back();
                vis[i]=0;
            }
        }
    }
    vector<vector<int>> permuteUnique(vector<int>& nums) {
        sort(nums.begin(), nums.end());
        vis.resize(nums.size(), 0);
        backtrace(nums, {});
        return ans;
    }
};

7、组合总和 leetcode
(元素无重可复选)

题解:

class Solution {
public:
    vector<vector<int> > ans;
    void backtrace(vector<int>& candidates, int idx, int sum, int target, vector<int> path)
    {
        if(sum>target)
        {
            return;
        }
        if(sum==target)
        {
            ans.push_back(path);
            return;
        }
        for(int i=idx;i<candidates.size();i++)
        {
            path.push_back(candidates[i]);
            backtrace(candidates, i, sum+candidates[i], target, path);
            path.pop_back();
        }
    }
    vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
        backtrace(candidates, 0, 0, target, {});
        return ans;
    }
};

8、总结
形式一、元素无重不可复选,即 nums 中的元素都是唯一的,每个元素最多只能被使用一次

/* 组合/子集问题回溯算法框架 */
void backtrack(int[] nums, int start) {
    // 回溯算法标准框架
    for (int i = start; i < nums.length; i++) {
        // 做选择
        track.addLast(nums[i]);
        // 注意参数
        backtrack(nums, i + 1);
        // 撤销选择
        track.removeLast();
    }
}

/* 排列问题回溯算法框架 */
void backtrack(int[] nums) {
    for (int i = 0; i < nums.length; i++) {
        // 剪枝逻辑
        if (used[i]) {
            continue;
        }
        // 做选择
        used[i] = true;
        track.addLast(nums[i]);

        backtrack(nums);
        // 撤销选择
        track.removeLast();
        used[i] = false;
    }
}

形式二、元素可重不可复选,即 nums 中的元素可以存在重复,每个元素最多只能被使用一次,其关键在于排序和剪枝

Arrays.sort(nums);
/* 组合/子集问题回溯算法框架 */
void backtrack(int[] nums, int start) {
    // 回溯算法标准框架
    for (int i = start; i < nums.length; i++) {
        // 剪枝逻辑,跳过值相同的相邻树枝
        if (i > start && nums[i] == nums[i - 1]) {
            continue;
        }
        // 做选择
        track.addLast(nums[i]);
        // 注意参数
        backtrack(nums, i + 1);
        // 撤销选择
        track.removeLast();
    }
}


Arrays.sort(nums);
/* 排列问题回溯算法框架 */
void backtrack(int[] nums) {
    for (int i = 0; i < nums.length; i++) {
        // 剪枝逻辑
        if (used[i]) {
            continue;
        }
        // 剪枝逻辑,固定相同的元素在排列中的相对位置
        if (i > 0 && nums[i] == nums[i - 1] && !used[i - 1]) {
            continue;
        }
        // 做选择
        used[i] = true;
        track.addLast(nums[i]);

        backtrack(nums);
        // 撤销选择
        track.removeLast();
        used[i] = false;
    }
}

形式三、元素无重可复选,即 nums 中的元素都是唯一的,每个元素可以被使用若干次,只要删掉去重逻辑即可

/* 组合/子集问题回溯算法框架 */
void backtrack(int[] nums, int start) {
    // 回溯算法标准框架
    for (int i = start; i < nums.length; i++) {
        // 做选择
        track.addLast(nums[i]);
        // 注意参数
        backtrack(nums, i);    // 这里和形式一不同,不需要i+1
        // 撤销选择
        track.removeLast();
    }
}


/* 排列问题回溯算法框架 */
void backtrack(int[] nums) {
    for (int i = 0; i < nums.length; i++) {
        // 做选择
        track.addLast(nums[i]);
        backtrack(nums);
        // 撤销选择
        track.removeLast();
    }
}

3.1.4 岛屿问题

DFS框架

// 二叉树遍历框架
void traverse(TreeNode root) {
    traverse(root.left);
    traverse(root.right);
}

// 二维矩阵遍历框架
void dfs(int[][] grid, int i, int j, boolean[][] visited) {
    int m = grid.length, n = grid[0].length;
    if (i < 0 || j < 0 || i >= m || j >= n) {
        // 超出索引边界
        return;
    }
    if (visited[i][j]) {
        // 已遍历过 (i, j)
        return;
    }
    // 进入节点 (i, j)
    visited[i][j] = true;
    dfs(grid, i - 1, j, visited); // 上
    dfs(grid, i + 1, j, visited); // 下
    dfs(grid, i, j - 1, visited); // 左
    dfs(grid, i, j + 1, visited); // 右
}

1、岛屿数量 leetcode

题解:DFS
时间复杂度:O(mn)
空间复杂度:O(mn)

class Solution {
public:
    vector<vector<int> > vis;
    int ans=0;
    int X[4] = {-1, 1, 0, 0};
    int Y[4] = {0, 0, -1, 1};
    void dfs(vector<vector<char>>& grid, int x, int y)
    {
        int m = grid.size();
        int n = grid[0].size();
        for(int i=0;i<4;i++)
        {
            int newX = x+X[i];
            int newY = y+Y[i];
            if(newX>=0 &&newX<m && newY>=0 && newY<n)
            {
                if(vis[newX][newY]==0 && grid[newX][newY]=='1')
                {
                    vis[newX][newY]=1;
                    dfs(grid, newX, newY);
                }
            }
        }
    }
    int numIslands(vector<vector<char>>& grid) {
        int m = grid.size();
        int n = grid[0].size();
        vis = vector<vector<int> > (m, vector<int>(n, 0));
        for(int i=0;i<m;i++)
        {
            for(int j=0;j<n;j++)
            {
                if(grid[i][j]=='1' && vis[i][j]==0)
                {
                    ans++;
                    dfs(grid, i, j);
                }
            }
        }
        return ans;
    }
};

题解:BFS
时间复杂度:O(mn)
空间复杂度:O(min(m,n))

class Solution {
public:
    int numIslands(vector<vector<char>>& grid) {
        int nr = grid.size();
        if (!nr) return 0;
        int nc = grid[0].size();

        int num_islands = 0;
        for (int r = 0; r < nr; ++r) {
            for (int c = 0; c < nc; ++c) {
                if (grid[r][c] == '1') {
                    ++num_islands;
                    grid[r][c] = '0';
                    queue<pair<int, int>> neighbors;
                    neighbors.push({r, c});
                    while (!neighbors.empty()) {
                        auto rc = neighbors.front();
                        neighbors.pop();
                        int row = rc.first, col = rc.second;
                        if (row - 1 >= 0 && grid[row-1][col] == '1') {
                            neighbors.push({row-1, col});
                            grid[row-1][col] = '0';
                        }
                        if (row + 1 < nr && grid[row+1][col] == '1') {
                            neighbors.push({row+1, col});
                            grid[row+1][col] = '0';
                        }
                        if (col - 1 >= 0 && grid[row][col-1] == '1') {
                            neighbors.push({row, col-1});
                            grid[row][col-1] = '0';
                        }
                        if (col + 1 < nc && grid[row][col+1] == '1') {
                            neighbors.push({row, col+1});
                            grid[row][col+1] = '0';
                        }
                    }
                }
            }
        }

        return num_islands;
    }
};

2、统计封闭岛屿的数目 leetcode

题解:
时间复杂度:O(mn)
空间复杂度:O(mn)

class Solution {
public:
    int X[4] = {-1, 1, 0, 0};
    int Y[4] = {0, 0, -1, 1};
    int ans=0;
    void dfs(vector<vector<int>>& grid, int x, int y)
    {
        int m = grid.size();
        int n = grid[0].size();
        for(int i=0;i<4;i++)
        {
            int newX = x+X[i];
            int newY = y+Y[i];
            if(newX>=0&&newX<m && newY>=0&&newY<n)
            {
                if(grid[newX][newY]==0)
                {
                    grid[newX][newY]=1;
                    dfs(grid, newX, newY);
                }
            }
        }
    }
    int closedisland(vector<vector<int>>& grid) {
        int m = grid.size();
        int n = grid[0].size();
        for(int i=0;i<m;i++)
        {
            if(grid[i][0]==0)
            {
                grid[i][0]=1;
                dfs(grid, i, 0);
            }
            if(grid[i][n-1]==0)
            {
                grid[i][n-1]=1;
                dfs(grid, i, n-1);
            }
        }
        for(int j=0;j<n;j++)
        {
            if(grid[0][j]==0)
            {
                grid[0][j]=1;
                dfs(grid, 0, j);
            }
            if(grid[m-1][j]==0)
            {
                grid[m-1][j]=1;
                dfs(grid, m-1, j);
            }
        }
        for(int i=0;i<m;i++)
        {
            for(int j=0;j<n;j++)
            {

                if(grid[i][j]==0)
                {
                    ans++;
                    grid[i][j]=1;
                    dfs(grid, i, j);
                }
            }
        }
        return ans;
    }
};

3、飞地的数量 leetcode

题解:
时间复杂度:O(mn)
空间复杂度:O(mn)

class Solution {
public:
    int X[4] = {-1, 1, 0, 0};
    int Y[4] = {0, 0, -1, 1};
    void dfs(vector<vector<int> >& grid, int x, int y)
    {
        int m = grid.size();
        int n = grid[0].size();
        grid[x][y]=0;
        for(int i=0;i<4;i++)
        {
            int newX = x+X[i];
            int newY = y+Y[i];
            if(newX>=0&&newX<m &&newY>=0&&newY<n && grid[newX][newY]==1)
            {
                dfs(grid, newX, newY);
            }
        }
    }
    int numEnclaves(vector<vector<int>>& grid) {
        int m = grid.size();
        int n = grid[0].size();
        for(int i=0;i<m;i++)
        {
            if(grid[i][0]==1)
            {
                dfs(grid, i, 0);
            }
            if(grid[i][n-1]==1)
            {
                dfs(grid, i, n-1);
            }
        }
        for(int j=0;j<n;j++)
        {
            if(grid[0][j]==1)
            {
                dfs(grid, 0, j);
            }
            if(grid[m-1][j]==1)
            {
                dfs(grid, m-1, j);
            }
        }
        int ans=0;
        for(int i=0;i<m;i++)
        {
            for(int j=0;j<n;j++)
            {
                if(grid[i][j]==1)
                {
                    ans++;
                }
            }
        }
        return ans;

    }
};

4、岛屿的最大面积 leetcode

题解:
时间复杂度:O(mn)
空间复杂度:O(mn)

class Solution {
public:
    int X[4] = {-1, 1, 0, 0};
    int Y[4] = {0, 0, -1, 1};
    
    int area=0;
    void dfs(vector<vector<int> >& grid, int x, int y)
    {
        int m = grid.size();
        int n = grid[0].size();
        grid[x][y]=0;
        for(int i=0;i<4;i++)
        {
            int newX = x+X[i];
            int newY = y+Y[i];
            if(newX>=0&&newX<m &&newY>=0&&newY<n && grid[newX][newY]==1)
            {
                area++;
                dfs(grid, newX, newY);
            }
        }
    }
    int maxAreaOfIsland(vector<vector<int>>& grid) {
        int m = grid.size();
        int n = grid[0].size();
        int ans=0;
        for(int i=0;i<m;i++)
        {
            for(int j=0;j<n;j++)
            {
                area=0;
                if(grid[i][j]==1)
                {
                    area++;
                    dfs(grid, i, j);
                }
                ans = max(ans, area);
            }
        }
        return ans;
    }
};

5、统计子岛屿 leetcode

题解:
时间复杂度:O(mn)
空间复杂度:O(mn)

class Solution {
public:
    int X[4] = {-1, 1, 0, 0};
    int Y[4] = {0, 0, -1, 1};
    void dfs(vector<vector<int>>& grid1, vector<vector<int>>& grid2, int x, int y, int& f_mark)
    {
        int m = grid2.size();
        int n = grid2[0].size();
        if(grid2[x][y]==1 && grid1[x][y]==1)
        {
            grid2[x][y]=0;
        }else{
            f_mark=0;
            return;
        }
        for(int i=0;i<4;i++)
        {
            int newX = x+X[i];
            int newY = y+Y[i];
            if(newX>=0&&newX<m&&newY>=0&&newY<n&&grid2[newX][newY]==1)
            {
                dfs(grid1, grid2, newX, newY, f_mark);
            }
        }
    }
    int countSubIslands(vector<vector<int>>& grid1, vector<vector<int>>& grid2) {
        int m = grid1.size();
        int n = grid1[0].size();
        int ans=0;
        for(int i=0;i<m;i++)
        {
            for(int j=0;j<n;j++)
            {
                if(grid2[i][j]==1)
                {
                    int f_mark=1;
                    dfs(grid1, grid2, i, j, f_mark);
                    if(f_mark)
                    {
                        ans++;
                    }
                }
            }
        }

        return ans;
    }
};

6、不同岛屿的个数 lintcode

题解:
时间复杂度:O(mn)
空间复杂度:O(mn)

比方说「下,右,撤销右,撤销下」和「下,撤销下,右,撤销右」显然是两个不同的遍历顺序,但如果不记录撤销操作,那么它俩都是「下,右」,成了相同的遍历顺序,显然是不对的。

class Solution {
public:
    /**
     * @param grid: a list of lists of integers
     * @return: return an integer, denote the number of distinct islands
     */
    int X[4] = {-1, 1, 0, 0};
    int Y[4] = {0, 0, -1, 1};
    void dfs(vector<vector<int>> &grid, int x, int y, string& path)
    {
        int m = grid.size();
        int n = grid[0].size();

        for(int i=0;i<4;i++)
        {
            int newX = x+X[i];
            int newY = y+Y[i];
            if(newX>=0&&newX<m&&newY>=0&&newY<n&&grid[newX][newY]==1)
            {
                path+=i+'0';   // 进入
                path+=',';
                grid[newX][newY]=0;
                dfs(grid, newX, newY, path);
                path+=-i+'0';   // 离开 
                path+=',';
            }
        }
    }
    int numberofdistinctIslands(vector<vector<int>> &grid) {
        // write your code here
        int m = grid.size();
        int n = grid[0].size();
        unordered_set<string> hashset;
        for(int i=0;i<m;i++)
        {
            for(int j=0;j<n;j++)
            {
                if(grid[i][j]==1)
                {
                    string path="";
                    grid[i][j]=0;
                    dfs(grid, i, j, path);
                    hashset.insert(path);
                }
            }
        }
        return hashset.size();
    }
};

3.1.5 解数独

leetcode

题解:
时间复杂度:O(mn)
空间复杂度:O(mn)

class Solution {
public:
    int row[9][10]={0};
    int col[9][10]={0};
    int grid[3][3][10]={0};
    vector<pair<int, int> > sequence;
    bool flag = false;
    void dfs(vector<vector<char>>& board, int cur)
    {
        if(cur==sequence.size())
        {
            flag=true;
            return;
        }
        int x = sequence[cur].first;
        int y = sequence[cur].second;
        for(int num=1;num<=9 && flag==false;num++)
        {
            if(row[x][num]==0 && col[y][num]==0 && grid[x/3][y/3][num]==0)
            {
                board[x][y] = num+'0';
                row[x][num]=1;
                col[y][num]=1;
                grid[x/3][y/3][num]=1;
                dfs(board, cur+1);
                // board[x][y] = '.';  这里回退不用把'.'填回去,这会把最后一次填的东西又盖住。因为如果填错数字,下一次可以直接覆盖的,主要有标记数组就行。
                row[x][num]=0;
                col[y][num]=0;
                grid[x/3][y/3][num]=0;
            }
        }
    }
    void solveSudoku(vector<vector<char>>& board) {
        for(int i=0;i<9;i++)
        {
            for(int j=0;j<9;j++)
            {
                if(board[i][j]=='.')
                {
                    sequence.push_back(make_pair(i, j));
                }else{
                    int num =  board[i][j]-'0';
                    row[i][num]=1;
                    col[j][num]=1;
                    grid[i/3][j/3][num]=1;
                }
            }
        }
        dfs(board, 0);
    }
};

3.1.6 括号生成

1、括号生成 leetcode

题解:
时间复杂度:O( 4 n ( n ) \frac{4^n}{\sqrt(n)} ( n)4n)
空间复杂度:O(n)

class Solution {
public:
    vector<string> ans;
    void backtrace(int left, int right, int n, string zuhe)
    {
        if(zuhe.size()==n*2)
        {
            ans.push_back(zuhe);
            return;
        }
        if(left<n)
        {
            backtrace(left+1, right, n, zuhe+'(');
        }
        if(right<left)
        {
            backtrace(left, right+1, n, zuhe+')');
        }
    }
    vector<string> generateParenthesis(int n) {
        backtrace(0, 0, n, "");
        return ans;
    }
};

3.1.7 BFS 算法解题套路框架

1、二叉树的最小深度 leetcode

题解:
时间复杂度:O(n)
空间复杂度:O(n)

class Solution {
public:
    int minDepth(TreeNode* root) {
        if(root==nullptr)
        {
            return 0;
        }
        queue<TreeNode*> q;
        q.push(root);
        int h = 1;
        while(!q.empty())
        {
            int size = q.size();
            while(size)
            {
                TreeNode* node = q.front();
                q.pop();
                size--;
                if(node->left==nullptr&&node->right==nullptr)
                {
                    return h;
                }
                if(node->left!=nullptr)
                {
                    q.push(node->left);
                }
                if(node->right!=nullptr)
                {
                    q.push(node->right);
                }
            }
            h++;
        }
        return h;
    }
};

2、打开转盘锁 leetcode

题解:
时间复杂度:O( b d ⋅ d 2 b^d\cdot d^2 bdd2 + md),其中 b是数字的进制,d是转盘数字的位数,m是数组deadends 的长度

空间复杂度:O( b d ⋅ d b^d\cdot d bdd + m)

class Solution {
public:
    char num_prev(char x)
    {
        return (x == '0' ? '9' : x - 1);
    }
    char num_succ(char x)
    {
        return (x == '9' ? '0' : x + 1);
    }
    vector<string> get(string& status)  // 枚举 status 通过一次旋转得到的数字
    {
        vector<string> ret;
        for (int i = 0; i < 4; ++i)    // 分别枚举每一位向前、向后能得到的数字
        {
            char num = status[i];
            status[i] = num_prev(num);
            ret.push_back(status);
            status[i] = num_succ(num);
            ret.push_back(status);
            status[i] = num;
        }
        return ret;
    }
    int openLock(vector<string>& deadends, string target) {
        if (target == "0000") {
            return 0;
        }

        unordered_set<string> dead(deadends.begin(), deadends.end());
        if (dead.count("0000")) {
            return -1;
        }

        queue<pair<string, int>> q;
        q.emplace("0000", 0);
        unordered_set<string> seen = {"0000"};

        while (!q.empty()) {
            auto [status, step] = q.front();
            q.pop();
            vector<string> next_status = get(status); // 获取在当前数字状态下,可能获得的下一次转动数字结果
            for (string str: next_status) {
                if (!seen.count(str) && !dead.count(str)) {
                    if (str == target) {
                        return step + 1;
                    }
                    q.emplace(str, step + 1);
                    seen.insert(str);
                }
            }
        }

        return -1;
    }
};

3、滑动谜题 leetcode

题解:
时间复杂度:O((mn)! * mn)
空间复杂度:O((mn)! * mn)

class Solution {
public:
    bool judge(vector<vector<int>>& board)
    {
        if(board[0][0]==1&&board[0][1]==2&&board[0][2]==3&&board[1][0]==4&&board[1][1]==5&&board[1][2]==0)
        {
            return true;
        }
        return false;
    }

    int X[4] = {-1, 1, 0, 0};
    int Y[4] = {0, 0, -1, 1};

    int slidingPuzzle(vector<vector<int>>& board) {
        unordered_map<string, int> vis;
        queue<vector<vector<int> > > q;
        q.push(board);
        int step=0;
        int ans=INT_MAX;
        while(q.size()!=0)
        {
            int size=q.size();
            while(size)
            {
                auto node = q.front();
                q.pop();
                if(judge(node))
                {
                    ans = min(ans, step);
                }
                int x,y;
                for(int i=0;i<2;i++)
                {
                    for(int j=0;j<3;j++)
                    {
                        if(node[i][j]==0)
                        {
                            x=i;
                            y=j;
                            break;
                        }
                    }
                }
                for(int i=0;i<4;i++)
                {
                    int newX = x+X[i];
                    int newY = y+Y[i];
                    if(newX>=0&&newX<2&&newY>=0&&newY<3)
                    {
                        swap(node[x][y], node[newX][newY]);
                        string str="";
                        for(int m=0;m<2;m++)
                        {
                            for(int n=0;n<3;n++)
                            {
                                str+=node[m][n]+'0';
                            }
                        }
                        if(vis.count(str)==0)
                        {
                            q.push(node);
                            vis[str]++;
                        }
                        swap(node[x][y], node[newX][newY]);
                    }
                }
                size--;
            }
            step++;
        }
        if(ans!=INT_MAX)
        {
            return ans;
        }else{
            return -1;
        }
    }
};

3.2 位运算

3.2.1 常用的位操作

几个有趣的位操作

  1. 利用异或操作 ^ 和空格进行英文字符大小写互换
('d' ^ ' ') = 'D'
('D' ^ ' ') = 'd'
  1. 判断两个数是否异号
int x = -1, y = 2;
boolean f = ((x ^ y) < 0); // true

int x = 3, y = 2;
boolean f = ((x ^ y) < 0); // false
  1. n & (n-1) 的运用:消除数字 n 的二进制表示中的最后一个 1

1、只出现一次的数字 leetcode

题解:
时间复杂度:O(n)
空间复杂度:O(1)

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int ans = nums[0];
        for(int i=1;i<nums.size();i++)
        {
            ans ^= nums[i];
        }
        return ans;
    }
};

2、丢失的数字 leetcode

题解:
时间复杂度:O(n)
空间复杂度:O(1)

class Solution {
public:
    int missingNumber(vector<int>& nums) {
        int sum = 0;
        for(int num:nums)
        {
            sum+=num;
        }
        int n = nums.size();
        return (n+1)*n/2 - sum;


    }
};

题解:位运算
时间复杂度:O(n)
空间复杂度:O(1)

class Solution {
public:
    int missingNumber(vector<int>& nums) {
        int res=0;
        int n = nums.size();
        res ^= n;
        for(int i=0;i<nums.size();i++)
        {
            res  = res ^ i ^ nums[i];
        }
        return res;

    }
};

3、位1的个数 leetcode

题解:
时间复杂度:O(n)
空间复杂度:O(1)

class Solution {
public:
    int hammingWeight(uint32_t n) {
        int ans=0;
        while(n!=0)
        {
            n = n&(n-1);
            ans++;
        }
        return ans;
    }
};

4、2 的幂 leetcode

题解:
时间复杂度:O(1)
空间复杂度:O(1)

class Solution {
public:
    bool isPowerOfTwo(int n) {
        if(n==0 || n<0)
            return false;
        n = n&(n-1);
        if(n==0)
        {
            return true;
        }else{
            return false;
        }

    }
};

3.2.2 阶乘

1、阶乘后的零 leetcode

题解:
时间复杂度:O(logn)
空间复杂度:O(1)

class Solution {
public:
    int trailingZeroes(int n) {
        int ans=0;
        while(n>0)
        {
            ans+=n/5;   // 每次把5的倍数中因子5的数量计算了,那么剩下就是更高层5的倍数中另外的5
            n/=5;
        }
        return ans;
    }
};

2、阶乘函数后 K 个零 leetcode

题解:用二分解方程
时间复杂度:O( l o g 2 K log^2 K log2K)
空间复杂度:O(1)

class Solution {
public:
    int f(long long n)
    {
        int num=0;
        while(n>0)
        {
            num+=n/5;
            n/=5;
        }
        return num;
    }
    int preimageSizefZF(int k) {
        long long r = (long long)5 * k;
        long long l = (long long)4 * k;
        while(l<=r)
        {
            long long mid = (r+l)/2;
            int num = f(mid);
            if(num==k)
            {
                return 5;
            }else if(num<k)
            {
                l=mid+1;
            }else{
                r=mid-1;
            }
        }
        return 0;

    }
};

3.2.3 素数

1、计数质数 leetcode

题解:
时间复杂度:O( nlog(logn) )
空间复杂度:O(n)

class Solution {
public:
    int countPrimes(int n) {
        if(n<2)
            return 0;
        vector<int> prime(n+1, 1);
        prime[0]=0;
        prime[1]=0;
        int ans=0;
        for(int i=2;i<n;i++)
        {
            if(prime[i]==1)
            {
                ans++;
                // 后面i的倍数,全是合数,不是质数.
                if((long long )i*i<n)
                {
                    for(long long j=i*i;j<n;j+=i)  // 从i*i开始遍历,2*i、3*i等都会被前面的数标记
                    {
                        prime[j]=0;
                    }
                }
            }
        }
        return ans;

    }
};

3.2.4 模幂运算

1、超级次方 leetcode

题解:
时间复杂度:O( ∑ i = 1 m l o g ( b i ) \sum_{i=1}^{m} log(b_i) i=1mlog(bi))
空间复杂度:O(1)

class Solution {
public:
    long pow(int a, int n)
    {
        long res=1;
        long x = (long) a;
        while(n!=0)
        {
            if(n&1)   // 表示二进制末尾有1,那就乘上一个x。如果是0的话,对应是乘1,没变化
            {
                res = res * x % 1337;
            }
            x = x*x % 1337;   // 不断平方,这样就相当于在二进制上累积
            n = n >> 1;
        }
        return res;
    }
    int superPow(int a, vector<int>& b) {
        long ans = 1;
        for(int i=b.size()-1;i>=0;i--)
        {
            ans = ans * pow(a, b[i]) % 1337;
            a = pow(a, 10);
        }
        return ans;
    }
};

3.2.5 错误的集合

1、错误的集合 leetcode

题解:
时间复杂度:O(n)
空间复杂度:O(1)

class Solution {
public:
    vector<int> findErrorNums(vector<int>& nums) {
        int n = nums.size();
        // 假设丢失的数字x,重复数字y
        int xorm = 0;
        for(int i=0;i<nums.size();i++)
        {
            xorm^=nums[i];
        }
        for(int i=1;i<=n;i++)
        {
            xorm^=i;
        }
        // 此时xorm,就是x^y。因为在这2n个数中,其他数字都出现2次,x出现1次,y出现3次
        int lowbit = xorm & (-xorm);  //lowbit 为 x 和 y 的二进制中的最低不同位
        int num1=0, num2=0;
        for(int i=0;i<nums.size();i++)
        {
            if(nums[i] & lowbit)
            {
                num1^=nums[i];
            }else{
                num2^=nums[i];
            }
        }
        for(int i=1;i<=n;i++)
        {
            if(i & lowbit)
            {
                num1 ^= i;
            }else{
                num2 ^= i;
            }
        }
        // 此时lowbit已经把2n个数,再次区分开。把x和y也区分开,但是不知道num1=x,还是num2=x。因此需要再次遍历数组
        for(int i=0;i<nums.size();i++)
        {
            if(nums[i]==num1)
            {
                return {num1, num2};
            }else if(nums[i]==num2)
            {
                return {num2, num1};
            }
        }
        return {};
    }
};

3.2.6 在无限序列中随机抽取元素

1、链表随机节点 leetcode

题解:
时间复杂度:O(n)
空间复杂度:O(1)

class Solution {
public:
    ListNode* root;
    Solution(ListNode* head) {
        root = head;
    }
    
    int getRandom() {
        ListNode* p = root;
        int len=1;
        int ans=p->val;
        while(p!=nullptr)
        {
            int idx = rand()%len + 1;    // 这里取值范围为[1,len]
            if(idx==len)   // 因此取到len的概率是 1/len,是等概率的
            {
                ans = p->val;   // 这里不能提前break,因为要考虑整个链表
            }
            p=p->next;
            len++;
        }
        return ans;
    }
};

2、随机数索引 leetcode

题解:水塘抽样
时间复杂度:O(n)
空间复杂度:O(1)

class Solution {
public:
    vector<int> vec;
    Solution(vector<int>& nums) {
        vec = nums;
    }
    
    int pick(int target) {
        int i=1, ans=0;
        for(int k=0;k<vec.size();k++)
        {
            if(vec[k]==target)
            {
                if(rand()%i==0)
                {
                    ans=k;
                }
                i++;
            }
        }
        return ans;
    }
};

3.3 经典面试题

3.3.1 分治算法

1、为运算表达式设计优先级 leetcode

题解:分治
时间复杂度:O(2^n)
空间复杂度:O(2^n)

class Solution {
public:
    vector<int> diffWaysToCompute(string expression) {
        vector<int> count;
        for(int i=0;i<expression.size();i++)
        {
            char c = expression[i];
            if(c=='+'||c=='-'||c=='*')
            {
                string left = expression.substr(0, i);
                string right = expression.substr(i+1);
                vector<int> l_res = diffWaysToCompute(left);   // 分别计算左右表达式的值
                vector<int> r_res = diffWaysToCompute(right);
                for(auto l : l_res)
                {
                    for(auto r: r_res)
                    {
                        if(c=='+')
                        {
                            count.push_back(l+r);
                        }else if(c=='-')
                        {
                            count.push_back(l-r);
                        }else{
                            count.push_back(l*r);
                        }
                    }
                }
            }
        }
        if(count.size()==0)  // 说明此时只有一个数字,没有运算符
        {
            count.push_back(stoi(expression));
        }
        return count;
    }
};

3.3.2 区间问题

1、删除被覆盖区间 leetcode

题解:贪心
时间复杂度:O(nlogn)
空间复杂度:O(logn)

class Solution {
public:
    int removeCoveredIntervals(vector<vector<int>>& intervals) {
        sort(intervals.begin(), intervals.end(), 
        [](const vector<int>& a, const vector<int>& b)
        {
            if(a[0]!=b[0])
            {
                return a[0]<b[0];     // 保证了当前区间的左端点一定小于等于下一个区间
            }else{
                return a[1]>b[1];
            }
        }
        );

        int ans=1;
        int right = intervals[0][1];
        for(int i=1;i<intervals.size();i++)
        {
            if(intervals[i][1]>right)    // 如果有新的右边界,那意味着新区间不能被覆盖
            {
                ans++;
                right = intervals[i][1];
            }
        }
        return ans;
    }
};

2、合并区间 leetcode

题解:
时间复杂度:O(nlogn)
空间复杂度:O(logn)

class Solution {
public:
    vector<vector<int>> merge(vector<vector<int>>& intervals) {
        sort(intervals.begin(), intervals.end(),
        [](const vector<int>& a, const vector<int>& b)
        {
            if(a[0]!=b[0])
            {
                return a[0]<b[0];
            }else{
                return a[1]>b[1];
            }
        }
        );
        vector<vector<int> > ans;
        int left=intervals[0][0];
        int right = intervals[0][1];
        for(int i=1;i<intervals.size();i++)
        {
            if(intervals[i][0]>right)  // 新起一个区间
            {
                ans.push_back({left, right});
                left = intervals[i][0];
                right = intervals[i][1];
            }else{
                if(intervals[i][1]>=right)   // 有交集,更新右边界
                {
                    right = intervals[i][1];
                }
            }
        }
        ans.push_back({left, right});
        return ans;
    }
};

3、区间列表的交集 leetcode

题解:
时间复杂度:O(min(n,m))
空间复杂度:O(1)

class Solution {
public:
    vector<vector<int>> intervalIntersection(vector<vector<int>>& firstList, vector<vector<int>>& secondList) {
        int i=0;
        int j=0;
        vector<vector<int> > ans;
        if(firstList.size()==0||secondList.size()==0)
        {
            return {};
        }
        while(i<firstList.size() && j<secondList.size())
        {
            int start_i = firstList[i][0];
            int end_i = firstList[i][1];
            int start_j = secondList[j][0];
            int end_j = secondList[j][1];
            int start, end;
            if(start_i<start_j)
            {
                start = start_j;
                if(end_i<end_j)
                {
                    end = end_i;
                    i++;
                }else{
                    end = end_j;
                    j++;
                }
            }else{
                start = start_i;
                if(end_i<end_j)
                {
                    end = end_i;
                    i++;
                }else{
                    end = end_j;
                    j++;
                }
            }
            if(start<=end)
                ans.push_back({start, end});
        }
        return ans;
    }
};

3.3.3 连续子序列的划分问题

1、分割数组为连续子序列 leetcode

题解:
时间复杂度:O(n)
空间复杂度:O(n)

class Solution {
public:
    bool isPossible(vector<int>& nums) {
        unordered_map<int, int> nc;   // 记录每个数有多少个
        unordered_map<int, int> tail;    // 记录序列的最后一个数字是什么
        for(auto v:nums)
        {
            nc[v]++;
        }
        for(auto num : nums)
        {
            if(nc[num]==0)       // 这个数已经被消耗完
            {
                continue;
            }else if(nc[num]>0 && tail[num-1]>0)   // 当前数还有,且存在前一个数的尾巴,那就优先加入前一个序列中
            {
                nc[num]--;
                tail[num-1]--;
                tail[num]++;
            }else if(nc[num]>0 && nc[num+1]>0 && nc[num+2]>0) // 否则连续取3个数形成一个队列
            {
                nc[num]--;
                nc[num+1]--;
                nc[num+2]--;
                tail[num+2]++;
            }else{       
                //只有检查到某个数时,这个数未被消耗完,且既不能和前面组成连续子序列,也不能和后面组成连续子序列时,无法分割
                return false;
            }
        }
        return true;
    }
};

3.3.4 烧饼排序算法

1、煎饼排序 leetcode

题解:
时间复杂度:O(nlogn)
空间复杂度:O(n)

class Solution {
public:
    vector<int> pancakeSort(vector<int>& arr) {
        vector<int> ans;
        vector<int> tmp = arr;
        sort(tmp.begin(), tmp.end());
        int last = tmp.size()-1;
        while(last!=0)
        {
            if(arr[last]==tmp[last])
            {
                last--;
            }else{
                int idx = 0;
                for(int i=0;i<arr.size();i++)
                {
                    if(arr[i]==tmp[last])
                    {
                        idx = i;
                        break;
                    }
                }
                ans.push_back(idx+1);
                ans.push_back(last+1);
                reverse(arr.begin(), arr.begin()+idx+1);
                reverse(arr.begin(), arr.begin()+last+1);
                last--;
            }
        }
        return ans;
    }
};

3.3.5 字符串乘法

leetcode

题解:
时间复杂度:O(mn)
空间复杂度:O(m+n)

class Solution {
public:
    string multiply(string num1, string num2) {
        if(num1=="0"||num2=="0")
        {
            return "0";
        }
        int m = num1.size();
        int n = num2.size();
        vector<int> str(m+n, 0);
        for(int i=0;i<m;i++)
        {
            for(int j=0;j<n;j++)
            {
                int a = num1[i] - '0';
                int b = num2[j] - '0';
                int res = a*b;
                str[i+j+1] += res;     // 两个数乘积,都累加在i+j+1上
                int idx = i+j+1;
                while(str[idx]>=10)     // 说明当前有进位,不断向前传递进位
                {
                    int carry = str[idx]/10;
                    str[idx]%=10;
                    idx--;
                    str[idx]+=carry;
                }
            }
        }
        int i=0;
        for(;i<m+n;i++)
        {
            if(str[i]!=0)     // 去掉前缀0
            {
                break;
            }
        }
        string ans="";
        while(i<m+n)
        {
            ans+=str[i]+'0';
            i++;
        }
        return ans;

    }
};

3.3.6 计算器

1、基本计算器 leetcode

题解:
时间复杂度:O(n)
空间复杂度:O(n)

class Solution {
public:
    int calculate(string s) {
        int n = s.size();
        long long num=0, res=0, curRes=0;
        char preOps='+';  // 记录前一个运算符
        for(int i=0;i<n;i++)
        {
            char c = s[i];
            if(isdigit(c))
            {
                num=num*10+c-'0';
            }else if(c=='(')
            {
                int j=i;
                int cnt=0; // 计算括号层数
                for(;i<n;i++)
                {
                    if(s[i]=='(')
                        cnt++;
                    if(s[i]==')')
                        cnt--;
                    if(cnt==0)
                        break;
                }
                num = calculate(s.substr(j+1, i-j-1));  // 递归计算括号里的数值
            }
            if(c=='+'||c=='-'||c=='*'||c=='/'||i==n-1)   // 遇到运算符时,先计算前面的结果
            {
                switch(preOps){
                    case '+':
                        curRes+=num;break;
                    case '-':
                        curRes-=num;break;
                    case '*':
                        curRes*=num;break;
                    case '/':
                        curRes/=num;break;
                }
                if(c=='+'||c=='-'||i==n-1)   // 当前运算符是+、-,那么可以直接计算到结果中,否则要等下一个数字先进行乘除运算
                {
                    res+=curRes;
                    curRes=0;
                }
                preOps=c;
                num=0;
            }
        }
        return res;
    }
};

3.3.6 接雨水问题

1、盛最多水的容器 leetcode

题解:
时间复杂度:O(n)
空间复杂度:O(1)

class Solution {
public:
    int maxArea(vector<int>& height) {
        int i=0, j=height.size()-1;
        int ans=0;
        while(i<j)
        {
            int min_h = min(height[i], height[j]);
            ans = max(ans, min_h*(j-i));
            if(height[i]<height[j])
            {
                i++;
            }else{
                j--;
            }
        }
        return ans;

    }
};

2、接雨水 leetcode

题解:
时间复杂度:O(n)
空间复杂度:O(1)

class Solution {
public:
    int trap(vector<int>& height) {
        int left=0;
        int right = height.size()-1;
        int leftMAX=0, rightMAX=0;
        int ans=0;
        while(left<right)
        {
            // 分别获得左、右的两侧屏障,每次总能获得屏障高度-当前height的雨水
            leftMAX = max(leftMAX, height[left]);
            rightMAX = max(rightMAX, height[right]);
            if(leftMAX<rightMAX)      // 这里比较的原因是,要取较小值屏障,这样保证有雨水
            {
                ans+=leftMAX-height[left];
                left++;
            }else{
                ans+=rightMAX - height[right];
                right--;
            }
        }
        return ans;
    }
};

3.3.7 括号相关的问题

1、有效的括号 leetcode

题解:
时间复杂度:O(n)
空间复杂度:O(n)

class Solution {
public:
    bool isValid(string s) {
        stack<char> stk;
        for(char c:s)
        {
            if(c=='(' || c=='[' || c=='{')
            {
                stk.push(c);
            }else{
                if(c==')')
                {
                    if(stk.empty() || stk.top()!='(')
                    {
                        return false;
                    }else{
                        stk.pop();
                    }
                }else if(c==']')
                {
                    if(stk.empty() || stk.top()!='[')
                    {
                        return false;
                    }else{
                        stk.pop();
                    }
                }else if(c=='}')
                {
                    if(stk.empty() || stk.top()!='{')
                    {
                        return false;
                    }else{
                        stk.pop();
                    }
                }
            }
        }
        return stk.empty();
    }
};

2、使括号有效的最少添加 leetcode

题解:
时间复杂度:O(n)
空间复杂度:O(1)

class Solution {
public:
    int minAddTomakeValid(string s) {
        int left=0, right=0;
        for(char c:s)
        {
            if(c=='(')
            {
                left++;
            }else{
                if(left>0)   // 有左括号的时候,才能用右括号抵消
                    left--;
                else{
                    right++;
                }
            }
        }
        return left+right;
    }
};

3、平衡括号字符串的最少插入次数 leetcode

题解:
时间复杂度:O(n)
空间复杂度:O(1)

class Solution {
public:
    int minInsertions(string s) {
        int ans=0;
        int need_right=0;
        for(int i=0;i<s.size();i++)
        {
            if(s[i]=='(')    
            {
                need_right+=2;  // 出现一个左括号,说明需要2个右括号
                if(need_right%2==1)    // 如果右括号为奇数,那么插入一个右括号让他满足抵消左括号。同时右括号的需求量--
                {
                    ans++;
                    need_right--;
                }
            }else if(s[i]==')')
            {
                need_right--;
                if(need_right==-1)   // 如果右括号需求量=-1,说明前面没有左括号,此时插入一个左括号,那么除了当前的右括号,还需要一个右括号,右括号的需求量=1
                {
                    ans++;
                    need_right=1;
                }
            }
        }
        return ans+need_right;

    }
};

3.3.8 完美矩形

leetcode

题解:
时间复杂度:O(n)
空间复杂度:O(n)

class Solution {
public:
    long to_key(int x, int y)
    {
        return (long)1000007*x+y;
    }
    void record(unordered_map<long, int>& mp, int x, int y)
    {
        long key=to_key(x, y);    // 二维坐标转化成一维
        if(mp.count(key))
        {
            mp.erase(key);
        }else{
            mp[key]=1;
        }
    }
    bool isRectangleCover(vector<vector<int>>& rectangles) {
        int min_x=INT_MAX, min_y=INT_MAX;
        int max_a=INT_MIN, max_b=INT_MIN;
        long total_area=0;
        unordered_map<long, int> mp;
        for(int i=0;i<rectangles.size();i++)
        {
            int x = rectangles[i][0];
            int y = rectangles[i][1];
            int a = rectangles[i][2];
            int b = rectangles[i][3];
            min_x = min(min_x, x);
            min_y = min(min_y, y);
            max_a = max(max_a, a);
            max_b = max(max_b, b);
            total_area+=(long)(a - x)*(b - y);

            record(mp, x, y);
            record(mp, x, b);
            record(mp, a, y);
            record(mp, a, b);
        }
        long best_area = (long)(max_a-min_x) * (max_b - min_y);
        if(best_area!=total_area)  // 大矩形的面积应该是几个矩形的面积和,如果不是,那必然错误
        {
            return false;
        }
        if(mp.size()==4 && mp.count(to_key(min_x, min_y)) && mp.count(to_key(min_x, max_b)) && mp.count(to_key(max_a, min_y)) && mp.count(to_key(max_a, max_b)))  // 最终完美矩形必须只有4个顶点,且在数组中出现过
        {
            return true;
        }else{
            return false;
        }


    }
};

3.3.9 调度考生的座位

leetcode

class ExamRoom {
public:
    set<int> sit;
    int n;
    ExamRoom(int N) {
        n=N;
    }
    
    int seat() {
        if(sit.size()==0)
        {
            sit.insert(0);
            return 0;
        }
        int pre = *sit.begin();    // 第一个有人的位置编号
        int idx=0, min_dis=pre;
        for(auto v : sit)
        {
            if((v-pre)/2 > min_dis)
            {
                idx = (v+pre)/2;
                min_dis = (v-pre)/2;
            }
            pre=v;
        }
        if(n-1-pre>min_dis)
        {
            idx=n-1;
        }
        sit.insert(idx);
        return idx;
    }
    
    void leave(int p) {
        sit.erase(p);

    }
};

/**
 * Your ExamRoom object will be instantiated and called as such:
 * ExamRoom* obj = new ExamRoom(n);
 * int param_1 = obj->seat();
 * obj->leave(p);
 */

3.3.10 二分查找高效判定子序列

1、判断子序列 leetcode

题解:
时间复杂度:O(n)
空间复杂度:O(1)

class Solution {
public:
    bool isSubsequence(string s, string t) {
        int i=0, j=0;
        while(i<s.size() && j<t.size())
        {
            if(s[i]==t[j])
            {
                i++;
                j++;
            }else{
                j++;
            }
        }
        return i==s.size();

    }
};

2、匹配子序列的单词数 leetcode

题解:
时间复杂度:O(S.length + ∑ i w o r d s i . l e n g t h \sum_i words_i.length iwordsi.length)
空间复杂度:O(words.length)

class Solution {
public:
// 例如,有字符串 S = 'dcaog':

// 初始化 heads = 'c' : ('cat', 'cop'), 'd' : ('dog',);
// 遍历 S[0] = 'd' 后,heads = 'c' : ('cat', 'cop'), 'o' : ('og',);
// 遍历 S[1] = 'c' 后,heads = 'a' : ('at',), 'o' : ('og', 'op');
// 遍历 S[2] = 'a' 后,heads = 'o' : ('og', 'op'), 't': ('t',) ;
// 遍历 S[3] = 'o' 后,heads = 'g' : ('g',), 'p': ('p',), 't': ('t',);
// 遍历 S[0] = 'g' 后,heads = 'p': ('p',), 't': ('t',)。


    int numMatchingSubseq(string S, vector<string>& words) {
        vector<queue<pair<int, int>>> buckets(26);
        for(int i = 0; i < words.size(); i++){
            buckets[words[i][0]-'a'].push({i, 0});   // 所有单词根据首字母不同放入不同的桶中,并记录在words中的索引 和 字母在这个单词的位置索引
        }
        int res = 0;
        for(auto c: S){
            queue<pair<int, int>> & q = buckets[c-'a'];
            for(int i = q.size(); i > 0; i--){
                auto [wordindex, posIndex] = q.front();
                q.pop();
                posIndex++;
                if(posIndex == words[wordindex].length()){  // 说明匹配到了一个单词
                    res++;
                }
                else{
                    buckets[words[wordindex][posIndex] - 'a'].push({wordindex, posIndex});
                }
            }
        }
        return res;
    }
};

相关文章

显卡天梯图2024最新版,显卡是电脑进行图形处理的重要设备,...
初始化电脑时出现问题怎么办,可以使用win系统的安装介质,连...
todesk远程开机怎么设置,两台电脑要在同一局域网内,然后需...
油猴谷歌插件怎么安装,可以通过谷歌应用商店进行安装,需要...
虚拟内存这个名词想必很多人都听说过,我们在使用电脑的时候...