leetcode总结

目录

1610. 可见点的最大数目 

链表

合并链表 148. 排序链表    21. 合并两个有序链表   23. 合并K个升序链表

反转链表  92. 反转链表 II  206. 反转链表  143. 重排链表

二叉树 —二叉搜索树

二叉树和双向链表结合 426. 将二叉搜索树转化为排序的双向链表

遍历二叉树 层序遍历 199. 二叉树的右视图

前序遍历搜索树255. 验证前序遍历序列二叉搜索树

递归求解 1214. 查找两棵二叉搜索树之和

 863. 二叉树中所有距离为 K 的结点

完全二叉树 222. 完全二叉树的节点个数

已知二叉树先序遍历跟后序遍历求二叉树的形状有多少种可能

 95. 不同的二叉搜索树 II

二叉树的公共祖先 236. 二叉树的最近公共祖先

572. 另一棵树的子树

二分法

搜索旋转数组 33. 搜索旋转排序数组 81. 搜索旋转排序数组 II

 寻找分割值、坐标 410. 分割数组的最大值 1011. 在 D 天内送达包裹的能力 1552. 两球之间的磁力 

寻找最小间隔匹配值 719. 找出第 k 小的距离对 786. 第 K 个最小的素数分数

排序

快排 179. 最大数

堆排 9-2合并K个升序数组 215. 数组中的第K个最大元素

 215. 数组中的第K个最大元素

动态规划

预处理再动规 

 1235. 规划兼职工作  354. 俄罗斯套娃信封问题 689. 三个无重叠子数组的最大和

 132. 分割回文串 II 

前缀和 

1504. 统计全 1 子矩形  

 689. 三个无重叠子数组的最大和 

直接动规

91. 解码方法

 664. 奇怪的打印机 221. 最大正方形 139. 单词拆分

回溯

 131. 分割回文串

DFS

 200. 岛屿数量 130. 被围绕的区域 329. 矩阵中的最长递增路径

  301. 删除无效的括号

  291. 单词规律 II

BFS和DFS结合 

 934. 最短的桥

判断是否是直角三角形

BFS  

 788 · 迷宫II

 127. 单词接龙

 1293. 网格中的最短路径

  1926. 迷宫中离入口最近的出口286. 墙与门 1926. 迷宫中离入口最近的出口 

 994. 腐烂的橘子

模拟

224. 基本计算器

单调栈

85. 最大矩形 84. 柱状图中最大的矩形 85可求出前缀行最大矩阵,计算最大的矩阵,调用84的函数

b[i]定义为a[i]左边离自己最近的比自己小的数字的下标

 1438. 绝对差不超过限制的最长连续子数组

贪心单调栈

 16 .移掉 K 位数字 

最小堆 

 295. 数据流的中位数

扫描线

 218. 天际线问题 遇到拐点进行统计

 850. 矩形面积 II 

贪心

  最多可以参加的会议数目

 1824. 最少侧跳次数

  45. 跳跃游戏 II

 1838. 最高频元素的频数    

 1423. 可获得的最大点数

 528. 按权重随机选择

递增数

35 按字典序输出1-n之间的数

投票法

字典序下一个排列

​​​​​​31. 下一个排列    

字典序

440. 字典序的第K小数字   

智力题

 1224. 最大相等频率

判断为树

KMP

滑窗法 

​​​​​​通过删除字母匹配到字典里最长单词  

340. 至多包含 K 个不同字符的最长子串    

给任意 str,删除任意字符后,求出最长的 4k 长度的 PONY(每个字母 k 长度,如 PPOONNYY)是多长。

数学题

10. 判断点在三角形内部

39. 圆内随机取点

概率题 放球

一道机器学习岗位面试题:平均要抛多少次硬币,才能出现连续两次正面向上?

 458. 可怜的小猪

圆上等概率的选3个点 构成锐角三角形的概率 (现场推导)


1610. 可见点的最大数目

    int visiblePoints(vector<vector<int>>& points, int ang, vector<int>& location) {
        int ans = 0;
        vector<double> angle;
        for (const auto& point : points) {
            int x = location[0] - point[0];
            int y = location[1] - point[1];
            if (x == 0 && y == 0) {
                ans++;
            } else {
                angle.push_back(atan2(y , x) * 180 / M_PI);
            }
        }

        sort(angle.begin(), angle.end());

        for(int i = 0, n = angle.size(); i < n; i++) {
            angle.emplace_back(angle[i] + 360);
        }

        int res = ans;
        for (int i = 0, j = 0; i < angle.size(); i++) {
            while (j < angle.size() && (angle[j] - angle[i] < ang)) {
                j++;
            }
            res = max(j - i + ans, res);
        }
        return res;
    }

链表

合并链表 148. 排序链表    21. 合并两个有序链表   23. 合并K个升序链表

    ListNode* mergeKLists(vector<ListNode*>& lists) {
        return merge(lists, 0, lists.size()-1);
    }

    ListNode* merge(vector<ListNode*>& lists, int l, int r)
    {
        if (l == r)
            return lists[l];
        if (l > r) // 考虑{[]}的情况,l=0, r=-1
            return nullptr;
        int mid = (l + r) >> 1;
        return mergeTwoLists(merge(lists, l, mid), merge(lists, mid+1, r));
    }

    ListNode* mergeTwoLists(ListNode* a, ListNode* b) {
        if (!a) return b;
        if (!b) return a;
        if (a->val < b->val) {
            a->next = mergeTwoLists(a->next, b);
            return a;
        } else {
            b->next = mergeTwoLists(a, b->next);
            return b;
        }
    }

反转链表  92. 反转链表 II  206. 反转链表  143. 重排链表

    ListNode* reverseList(ListNode* head) {
        ListNode* cur = head;
        ListNode* pre = nullptr;
        while (cur) {
            ListNode* next = cur->next;
            cur->next = pre;
            pre = cur;
            cur = next;
        }
        return pre;
    }
翻转区间链表
    ListNode* cur = pre->next;
    ListNode* next = new ListNode();
    for (int i = 0; i < right - left; i++) {
        next = cur->next;
        cur->next = next->next;
        next->next = pre->next;
        pre->next = next;
    }
    return dummyNode->next;

二叉树 —二叉搜索树

二叉树和双向链表结合 426. 将二叉搜索树转化为排序的双向链表

    Node* first = NULL;
    Node* last = NULL;
    Node* treeToDoublyList(Node* root) {
        if (!root) return NULL;
        helper(root);
        last->right = first;
        first->left = last;
        return first;        
    }

    void helper(Node* node) {
        if (node) {
            helper(node->left);

            if (last) {
                last->right = node;
                node->left = last;
            } else {
                first = node;
            }
            last = node;

            helper(node->right);
        }
    }

遍历二叉树 层序遍历 199. 二叉树的右视图

    vector<int> rightSideView(TreeNode* root) {
        queue<TreeNode*> que;
        if (root != NULL) que.push(root);
        vector<int> result;
        while (!que.empty()) {
            int size = que.size();
            for (int i = 0; i < size; i++) {
                TreeNode* node = que.front();
                que.pop();
                if (i == (size - 1)) result.push_back(node->val); // 将每一层的最后元素放入result数组中
                if (node->left) que.push(node->left);
                if (node->right) que.push(node->right);
            }
        }
        return result;
    }

前序遍历搜索树255. 验证前序遍历序列二叉搜索树

    bool verifyPreorder(vector<int>& preorder) {
        if(preorder.empty()) return true;

        stack<int> st;
        int root = INT_MIN;
        for(int num: preorder){
            if(num<root) return false;
            while(!st.empty() && num > st.top()){
                root = st.top();
                st.pop();
            }
            st.push(num);
        }
        return true;
    }

递归求解 1214. 查找两棵二叉搜索树之和

    bool twoSumBSTs(TreeNode* root1, TreeNode* root2, int target) {
        if (root1 == nullptr) return false;
        return helper(root2, target - root1->val) || twoSumBSTs(root1->left, root2, target) || twoSumBSTs(root1->right, root2, target);
    }

    bool helper(TreeNode* root,int target) {
        if (root) {
            if (target == root->val) return true;

            if (target < root->val) {
                return helper(root->left, target);
            }
            
            if (target > root->val) {
                return helper(root->right, target);
            }
        }
        return false;
    }

 863. 二叉树中所有距离为 K 的结点

    unordered_map<int, TreeNode*> parents;
    vector<int> ans;

    void findParents(TreeNode* node) {
        if (node->left != nullptr) {
            parents[node->left->val] = node;
            findParents(node->left);
        }
        if (node->right != nullptr) {
            parents[node->right->val] = node;
            findParents(node->right);
        }
    }

    void findAns(TreeNode* node, TreeNode* from, int depth, int k) {
        if (node == nullptr) {
            return;
        }
        if (depth == k) {
            ans.push_back(node->val);
            return;
        }
        if (node->left != from) {
            findAns(node->left, node, depth + 1, k);
        }
        if (node->right != from) {
            findAns(node->right, node, depth + 1, k);
        }
        if (parents[node->val] != from) {
            findAns(parents[node->val], node, depth + 1, k);
        }
    }

public:
    vector<int> distanceK(TreeNode* root, TreeNode* target, int k) {
        // 从 root 出发 DFS,记录每个结点的父结点
        findParents(root);

        // 从 target 出发 DFS,寻找所有深度为 k 的结点
        findAns(target, nullptr, 0, k);

        return ans;
    }

完全二叉树 222. 完全二叉树的节点个数

    int countNodes(TreeNode* root) {
        if (root == nullptr) return 0;
        TreeNode* left = root->left;
        TreeNode* right = root->right;
        int leftHeight = 0, rightHeight = 0; // 这里初始为0是有目的的,为了下面求指数方便
        while (left) {  // 求左子树深度
            left = left->left;
            leftHeight++;
        }
        while (right) { // 求右子树深度
            right = right->right;
            rightHeight++;
        }
        if (leftHeight == rightHeight) {
            return (2 << leftHeight) - 1; // 注意(2<<1) 相当于2^2,所以leftHeight初始为0
        }
        return countNodes(root->left) + countNodes(root->right) + 1;
    }

已知二叉树先序遍历跟后序遍历求二叉树的形状有多少种可能

//已知二叉树先序遍历跟后序遍历求二叉树的形状有多少种可能
//思路:
//      ①设共有n个结点;(设二叉树先序遍历序列为A[n],后序遍历序列为B[n])
//      ②遍历二叉树先序遍历,从第i(i>=2)个开始,使用find函数找到后序遍历B[i]中A[i]的位置x
//      ③比较A[i] == B[x] && A[i+1] == B[ x - 1 ](x>=1) 是否成立
//      ③如果成立则证明先序遍历第i+1个位置是一个单叶节点,也即二叉树的情况多了2倍

#include <iostream>
#include <string>
using namespace::std;
int main() {
    string pre, post;
    cin >> pre >> post;

    int length = pre.size();
    int single_leaf_node_num = 1;

    for (int i = 0; i + 1 < length; ++i) {
        int j = post.find(pre[i]);

        if (j < 1) continue;

        if (pre[i + 1] == post[j - 1])
            single_leaf_node_num *= 2;
    }
    cout << single_leaf_node_num;
    return 0;
}

 95. 不同的二叉搜索树 II

    vector<TreeNode*> generateTrees(int n) {
        if (!n) {
            return {};
        }
        return generateTrees(1, n);
    }

    vector<TreeNode*> generateTrees(int start, int end) {
        if (start > end) {
            return {nullptr};
        }

        vector<TreeNode*> allTrees;
        for (int i = start; i <= end; i++) {
            vector<TreeNode*> left = generateTrees(start, i-1);
            vector<TreeNode*> right = generateTrees(i+1, end);

            for (auto& l : left) {
                for (auto& r : right) {
                    TreeNode* Tree = new TreeNode(i);
                    Tree->left = l;
                    Tree->right = r;
                    allTrees.push_back(Tree);
                }
            }
        }
        return allTrees;
    }

二叉树的公共祖先 236. 二叉树的最近公共祖先

    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if(root==NULL)
        {
            return root;//空的找个屁,既然有那肯定是根
        }
        if(root==p||root==q)
        {
            return root;//既然肯定有,而且有一个就是根,那么直接返回根,
        }
        //如果上面都不是,那么肯定在左右两边
        //根节点往下一层挪,直到根两个中一个相等为止
        TreeNode* left=lowestCommonAncestor(root->left,p,q);
        TreeNode* right=lowestCommonAncestor(root->right,p,q);
        //有三种情况,如果同在左边,那么肯定left不是空,右边为空
        //如果同在右边,那么right不是空,左边为空
        //如果两边都不为空。那么肯定是根节点
        if(left&&right) return root;
        return left? left:right;
    }

572. 另一棵树的子树

bool isSubtree(TreeNode* root, TreeNode* subRoot) {
    if (subRoot == nullptr) return true;
    if (root == nullptr) return false;
    return check(root, subRoot) || isSubtree(root->left, subRoot) || isSubtree(root->right, subRoot); 
}
bool check(TreeNode* root, TreeNode* subRoot) {
    if (root == nullptr && subRoot == nullptr) return true;
    if (!root || !subRoot) return false;
    if (root->val != subRoot->val) return false;
    return check(root->left, subRoot->left) && 
           check(root->right, subRoot->right);
}

二分法

    int left = 0, right = n-1;
    int ans = 0;
    while (left <= right) {
        int mid = left + (right - left) / 2;
        if (check(nums, mid, k)) {
            ans = mid;
            right = mid - 1;
        } else {
            left = mid + 1;
        }
    }

搜索旋转数组 33. 搜索旋转排序数组 81. 搜索旋转排序数组 II

      bool search(vector<int> &nums, int target) {
        int n = nums.size();
        if (n == 0) {
            return false;
        }
        if (n == 1) {
            return nums[0] == target;
        }
        int l = 0, r = n - 1;
        while (l <= r) {
            int mid = (l + r) / 2;
            if (nums[mid] == target) {
                return true;
            }
            if (nums[l] == nums[mid] && nums[mid] == nums[r]) {
                ++l;
                --r;
            } else if (nums[l] <= nums[mid]) {
                if (nums[l] <= target && target < nums[mid]) {
                    r = mid - 1;
                } else {
                    l = mid + 1;
                }
            } else {
                if (nums[mid] < target && target <= nums[n - 1]) {
                    l = mid + 1;
                } else {
                    r = mid - 1;
                }
            }
        }
        return false;
    }

 寻找分割值、坐标 410. 分割数组的最大值 1011. 在 D 天内送达包裹的能力 1552. 两球之间的磁力 

    int splitArray(vector<int>& nums, int m) {
        int n = nums.size();
        int maxNum = 0;
        int sum = 0;
        for (int i = 0; i < n; i++) {
            maxNum = max(maxNum, nums[i]);
            sum += nums[i];
        }

        int left = maxNum, right = sum;
        while (left <= right) {
            int mid = left + (right - left) / 2;
            if (check(nums, mid, m)) {
                right = mid-1;
            } else {
                left = mid+1;
            }
        }
        return left;
    }

    bool check(vector<int>& nums, int val, int m) {
        long long sum = 0;
        int cnt = 1;
        for (int i = 0; i < nums.size(); i++) {
            sum += nums[i];
            if (sum > val) {
                cnt++;
                sum = nums[i];
            }
        }
        return cnt <= m;
    }

寻找最小间隔匹配值 719. 找出第 k 小的距离对 786. 第 K 个最小的素数分数

    vector<int> kthSmallestPrimeFraction(vector<int>& A, int K) {
        double left = 0.0;
        double right = 1.0;
        int n = A.size();
        while(left<right){
            double mid = (right+left)/2.0;
            int cnt = 0;
            vector<int> maxi = {0, 1};
            int j = 0;
            for(int i=0;i<n;i++){
                while(j<n && A[i]>=mid*A[j])    j++;
                cnt += n-j;
                if(j<n && maxi[0]*A[j] < A[i]*maxi[1])  maxi = {A[i], A[j]};
            }
            if(cnt==K)  return maxi;
            if(cnt<K)   left = mid;
            else right = mid;
        }
        return {};
    }

    int smallestDistancePair(vector<int>& nums, int k) {
        sort(nums.begin(), nums.end());
        int n = nums.size();
        int left = 0, right = nums[n-1] - nums[0];
        while (left <= right) {
            int mid = left + (right - left) / 2;
            if (check(nums, mid, k)) {
                right = mid - 1;
            } else {
                left = mid + 1;
            }
        }
        return left;
    } 

    bool check(vector<int>& nums, int dis, int k) {
        int n = nums.size(), j = 1;
        int cnt = 0; // 记录小于等于当前构造解 dist 的距离对数量
        for(int i = 0; i < n; i++) {
            while(j < n && nums[j] - nums[i] <= dis) j++;
            cnt += j - i - 1; // 处于 [i + 1, j] 中的数与 a[i]的距离均小于 dist
        }
        return cnt >= k;
    }

排序

快排 179. 最大数

    string largestNumber(vector<int>& nums) {
        vector<string> str;
        for (int i = 0; i < nums.size(); i++) {
            str.push_back(to_string(nums[i]));
        }

        quickSort(0, str.size()-1, str);
        string res = "";
        for (auto& num : str) {
            res += num;
        }
        int k = 0;
        while (res[k] == '0' && k < res.size()-1) {
            k++;
        }
        return res.substr(k);
    }

    void quickSort(int left, int right, vector<string>& nums) {
        if (left >= right) {
            return;
        }

        int i = left, j = right;
        while (i < j) {
            while (i < j && nums[j] + nums[left] <= nums[left] + nums[j]) {
                j--;
            }
            while (i < j && nums[i] + nums[left] >= nums[left] + nums[i]) {
                i++;
            }
            swap(nums[i], nums[j]);
        }
        swap(nums[left], nums[i]);
        quickSort(left, i-1, nums);
        quickSort(i+1, right, nums);
    }

堆排 9-2合并K个升序数组 215. 数组中的第K个最大元素

class Node { 
public:      
    int row, col, val;     
    Node (int r, int c, int v) : row(r), col(c), val(v) {};     
    bool operator < (const Node &obj) const {
         return val > obj.val;     
    } 
};
vector<int> mergekSortedArrays(vector<vector<int>>& arrays) {
     vector<int> result;         
     if (arrays.empty()) return result;          
     priority_queue<Node> queue;         
     for (int i = 0; i < arrays.size(); i++) {             
         if (!arrays[i].empty())                 
         queue.push(Node(i, 0, arrays[i][0]));         
     }          
     while (!queue.empty()) {             
         Node curr = queue.top();             
         queue.pop();             
         result.push_back(curr.val);             
         if (curr.col + 1 < arrays[curr.row].size())
             queue.push(Node(curr.row, curr.col + 1,
                        arrays[curr.row][curr.col + 1]));         
    }                  
    return result;     
}

 215. 数组中的第K个最大元素

    int findKthLargest(vector<int>& nums, int k) {
        int heapsize = nums.size();
        BuildHeap(nums, heapsize);
        for (int i = nums.size()-1; i >= nums.size() - k + 1; i--) {
            swap(nums[0], nums[i]);
            Heapify(nums, 0, --heapsize);
        }
        return nums[0];
    }
    void Heapify(vector<int>& nums, int index, int heapsize) {
        int largest = index;
        int left = index * 2 + 1;
        int right = index * 2 + 2;
        if (left < heapsize && nums[left] > nums[largest]) {
            largest = left;
        }
        if (right < heapsize && nums[right] > nums[largest]) {
            largest = right;
        }
        if (largest != index) {
            swap(nums[largest], nums[index]);
            Heapify(nums, largest, heapsize);
        }
    }
    void BuildHeap(vector<int>& nums, int heapsize) {
        for (int i = heapsize / 2; i >= 0; i--) {
            Heapify(nums, i, heapsize);
        }
    }

动态规划

预处理再动规 

 1235. 规划兼职工作  354. 俄罗斯套娃信封问题 689. 三个无重叠子数组的最大和

    struct Work {
        int start, end, profit;
        Work(int _start, int _end, int _profit) : start(_start), end(_end), profit(_profit) {}
    };
public:
    int jobScheduling(vector<int>& startTime, vector<int>& endTime, vector<int>& profit) {
        int n = startTime.size();
        vector<Work> mission(n+1, {0, 0, 0});
        for (int i = 0; i < n; i++) {
            mission[i] = Work(startTime[i], endTime[i], profit[i]);
        }
        
        sort(mission.begin(), mission.end(), [&](const Work& a, const Work& b) {
            return a.end < b.end;
        });

        vector<int> prev(n+1);
        for (int i = 1; i <= n; i++) {
            for (int j = i-1; j >= 0; j--) {
                if (mission[j].end <= mission[i].start) {
                    prev[i] = j;
                    break;
                }
            }
        }

        vector<int> dp(n+1);

        for (int i = 1; i <= n; i++)
            dp[i] = max(dp[i - 1], mission[i].profit + dp[prev[i]]);
        return dp[n];
    }

 132. 分割回文串 II 

    int minCut(string s) {
        if (s.size() == 0) return 0;
        int n = s.size();

        vector<int> dp(n, n);
        for (int i = 0; i < n; i++) {
            helper(dp, s, i, i);
            helper(dp, s, i, i+1);
        }
        return dp[n-1];
    }

    void helper(vector<int>& dp, string s, int i, int j) {
        int n = s.size();

        while (i >= 0 && j < n && s[i] == s[j]) {
            dp[j] = min(dp[j], (i==0 ? -1 : dp[i-1]) + 1);
            i--;
            j++;
        }
    }

前缀和 

如果一个串只由1和0组成,并且其中1和0的个数相等,我们称之为偶子串。给出一个只由1和0组成的串,求这个串中的子串中,最长的偶子串。

统计前缀和得值,如果有相同得前缀和,说明这两次相同前缀和之间为0,即最长01字串

int main() {
    vector<int> nums = {1,0,0,1,1,1,0,1,1,0,0,1};
    int n = nums.size();
    for (int i = 0; i < n; i++) {
        if (nums[i] == 0) nums[i] = -1;
    }

    vector<int> sum(n+1, 0);
    for (int i = 1; i <= n; i++) {
        sum[i] = sum[i-1] + nums[i-1];
    }
    unordered_map<int, int> unmap;
    int ans = 0;
    for (int i = 1; i <= n; i++) {
        if (!unmap.count(sum[i])) unmap[sum[i]] = i;
        if (sum[i] == 0) {
            ans = max(ans, i);
        } else {
            ans = max(ans, i - unmap[sum[i]]);
        }
    }
    cout << ans;
}

1504. 统计全 1 子矩形  

    int numSubmat(vector<vector<int>>& mat) {
        int m = mat.size();
        int n = mat[0].size();
        int sum = 0;

        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (mat[i][j] == 0) continue;
                else {// 当mat[i][j]为1, 是该行的第一位时赋1, 否则则为前一位+1
                    mat[i][j] = j >= 1 ? mat[i][j-1]+1 : 1;
                }
            }
        }

        for (int i = m - 1; i >= 0; i--) {
            for (int j = n - 1; j >= 0; j--) {
                if (mat[i][j] == 0) continue;
                int level = i; //记录层数
                int minHigh = mat[i][j]; // // 用于记录向上寻找过程中的最小值
                while (level >= 0 && mat[level][j] != 0) {// 向上寻找直到 处于第一层 或者 遇到0
                    minHigh = min(minHigh, mat[level][j]); // 更新最小值, 即以最短的为标准
                    sum += minHigh;
                    level--;
                }
            }
        }

        return sum;

 689. 三个无重叠子数组的最大和 

689. 三个无重叠子数组的最大和 (前缀和)
class Solution {
public:
    vector<int> maxSumOfThreeSubarrays(vector<int>& nums, int k) {
        vector<int> sum;
        int cur = 0;
        for(int i = 0; i < k; ++i){
            cur += nums[i];
        }
        sum.push_back(cur);
        for(int i = k; i < nums.size(); ++i){
            cur += nums[i] - nums[i - k];
            sum.push_back(cur);
        }
        int n = sum.size();
        vector<int> left(n, 0), right(n, n - 1);
        for(int i = 1; i < n; ++i){
            if(sum[i] > sum[left[i - 1]]) left[i] = i;
            else left[i] = left[i - 1];
        }
        for(int i = n - 2; i >= 0; --i){
            if(sum[i] >= sum[right[i + 1]]) right[i] = i;
            else right[i] = right[i + 1];
        }
        int mx = 0;
        vector<int> ans(3);
        for(int i = k; i < n - k; ++i){
            if(mx < sum[i] + sum[left[i - k]] + sum[right[i + k]]){
                mx = sum[i] + sum[left[i - k]] + sum[right[i + k]];
                ans = {left[i - k], i, right[i + k]};
            }
        }
        return ans;
    }
};


直接动规

91. 解码方法

    int numDecodings(string& s) {
        int n = s.length();
        vector<int> dp(n+2);
        dp[n] = 1;
        if (s[n-1] != '0') {
            dp[n-1] = 1;
        }
        for (int i = n-2; i >= 0; i--) {
            if (s[i] == '0') continue;

            if ((s[i] - '0' ) * 10 + (s[i+1] - '0') <= 26) {
                dp[i] = dp[i+1] + dp[i+2];
            } else {
                dp[i] += dp[i+1];
            }
        }
        return dp[0];
    }

 664. 奇怪的打印机 221. 最大正方形 139. 单词拆分

    int strangePrinter(string s) {
        int n = s.size();
        vector<vector<int>> dp(n, vector<int>(n, INT_MAX));
        for(int i = n-1; i >= 0; --i)
        {
            dp[i][i] = 1;
            for(int j = i + 1; j < n; ++j)
            {
                if(s[i] == s[j])
                    dp[i][j] = dp[i][j-1];
                else //枚举区间内所有的可能性,取最优
                    for(int k = i; k < j; ++k)
                        dp[i][j] = min(dp[i][j], dp[i][k] + dp[k+1][j]);
            }
        }
        return dp[0][n-1];
    }

回溯

 131. 分割回文串

class Solution {
    vector<vector<string>> res;
    vector<string> path;
public:
    vector<vector<string>> partition(string s) {
        backtracking(s, 0);
        return res;
    }

    void backtracking(string& s, int start_index) {
        if (start_index == s.size()) {
            res.push_back(path);
        }

        for (int i = start_index; i < s.size(); i++) {
            if (isPartition(s, start_index, i)) {
                path.push_back(s.substr(start_index, i-start_index+1));
            } else {
                continue;
            }
            backtracking(s, i+1);
            path.pop_back();
        }
    }

    bool isPartition(string& s, int start, int end) {
        while (start < end) {
            if (s[start] == s[end]) {
                start++;
                end--;
            } else {
                return false;
            }
        }
        return true;
    }
};

DFS

 200. 岛屿数量 130. 被围绕的区域 329. 矩阵中的最长递增路径

    int numIslands(vector<vector<char>>& grid) {
        int count = 0;
        vector<vector<bool>> visited(grid.size(), vector<bool>(grid[0].size(), false));
        for (int i = 0; i < grid.size(); i++) {
            for (int j = 0; j < grid[0].size(); j++) {
                if (grid[i][j] == '1' && !visited[i][j]) {
                    dfs(grid, i, j, visited);
                    count++;
                }
            }
        }
        return count;
    }

    void dfs(vector<vector<char>>& grid, int x, int y, vector<vector<bool>>& visited) {
        if (x < 0 || y < 0 || x >= grid.size() 
        || y >= grid[0].size() || grid[x][y]  == '0' || visited[x][y]) return ;
        
        visited[x][y] = true;
        dfs(grid, x, y+1, visited);
        dfs(grid, x, y-1, visited);
        dfs(grid, x+1, y, visited);
        dfs(grid, x-1, y, visited);
    }

  301. 删除无效的括号

    unordered_set<string> ans;//答案中可能会重复 需要去重 如(() 删除第一个( 或删除第二个( 都是() 这也提示了优化方法
    void dfs(string &s, int u, string res, int cnt, int l, int r){//cnt为当前左-右的值 l为需要删除的左括号数量 r为右
        //cout<<res<<endl;
        if(u==s.size()){
            if(!cnt) ans.insert(res);
            return;
        }
        if(s[u]=='('){//在合法的条件下考虑删除
            dfs(s, u+1, res+'(', cnt+1, l, r);//不删
            if(l > 0) dfs(s,u+1,res,cnt,l-1,r);//删当前的左括号
        }
        else if(s[u]==')'){//在合法的条件下考虑删除
            if(cnt > 0) dfs(s,u+1,res+')',cnt-1,l,r);//不删
            if(r > 0) dfs(s,u+1,res,cnt,l,r-1);//删当前的右括号
        }
        else dfs(s,u+1,res + s[u],cnt,l,r);
    }
    vector<string> removeInvalidParentheses(string s) {
        int l = 0, r = 0;//l为当前左括号-右括号的值 r为需要删掉的右括号的值
        for(auto v : s){
            if(v=='(') l++;
            else if(v==')'){
                if(l <= 0) r++;//此时无效 右括号 必删
                else l--;//右括号+1 则左-右的值需要--   
            }  
        }
        //此时的l为需要删除的左括号数量 r为需要删除的右括号数量
        dfs(s,0,"",0,l,r);
        vector<string> ret;
        for(auto v : ans) ret.push_back(v);
        return ret;
    }

  291. 单词规律 II

    bool wordPatternMatch(string pattern, string str) {
        if(pattern.size() > str.size())
            return false;
        // 记录字符对应的字符串
        unordered_map<char, string> hash;
        // 记录已经匹配的字符串,因为a、b不能匹配一样的字符串
        unordered_set<string> se;
        return track_back(pattern, str, 0, 0, hash, se);
    }

    bool track_back(string &pattern, string &str, int indexs, int indexp, unordered_map<char, string> &hash, unordered_set<string> &se){
        // 如果两个索引都达到末尾,那么久说明找到了一个正解
        // 如果是让你找到所有匹配的情况,那么就将bool换成void
        if(indexs == str.size() && indexp == pattern.size())
            return true;
        else if(indexs == str.size() || indexp == pattern.size())
            return false;
        if(hash.count(pattern[indexp])){//exist
            string temp = hash[pattern[indexp]];
            // 将哈希表中的字符串和str中的字符进行匹配
            for(int i = 0; i < temp.size(); i++)
                if(indexs + i >= str.size() || str[indexs + i] != temp[i])
                    return false;
            // 成功匹配,就继续往后进行遍历回溯
            return track_back(pattern, str, indexs + temp.size(), indexp + 1, hash, se);
        }
        else{//don't exist
            // 尝试各种长度的字符串,但最后要留下的字符串的长度要至少等于pattern中剩下的单词
            for(int i = 0; i + indexs <= str.size() - (pattern.size() - indexp); i++){
                string temp = str.substr(indexs, i + 1);
                // 如果有别的字符匹配了这个字符串,那么这个字符就不能这样匹配
                if(se.count(temp))
                    continue;
                // 在匹配过的字符串集合中插入这个字符串
                se.insert(temp);
                // 哈希表中插入
                hash[pattern[indexp]] = temp;
                // 向后进行遍历
                if(track_back(pattern, str, indexs + i + 1, indexp + 1, hash, se)){
                    return true;
                }
                // 第一个回溯的地方,这个字符匹配方式不对,那么就变换匹配法,将这个匹配法的字符串删除掉
                se.erase(temp);
            }
            // 第二个回溯的地方,将哈希表中的这个字符匹配数据删除掉
            hash.erase(pattern[indexp]);
            return false;
        }
        
    }

BFS和DFS结合 

 934. 最短的桥

class Solution {
public:
    vector<vector<int>> direction = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
    void dfs(queue<pair<int, int>>& points, vector<vector<int>>& grid, int i, int j) {
        if (i < 0 || j < 0 || i >= grid.size() || j >= grid[0].size() || grid[i][j] == 2) return;
        if (grid[i][j] == 0) {
            points.push({i, j});
            return;
        }
        grid[i][j] = 2;
        dfs(points, grid, i-1, j);
        dfs(points, grid, i+1, j);
        dfs(points, grid, i, j-1);
        dfs(points, grid, i, j+1);
    }
    int shortestBridge(vector<vector<int>>& grid) {
        int m = grid.size();
        int n = grid[0].size();
        queue<pair<int, int>> points;
        bool flag = false;
        for (int i = 0; i < m; i++) {
            if (flag) break;
            for (int j = 0; j < n; j++) {
                if (grid[i][j] == 1) {
                    dfs(points, grid, i, j);
                    flag = true;
                    break;
                }
            }
        }
        
        int step = 0;
        while (!points.empty()) {
            step++;
            int size = points.size();
            while (size--) {
                auto [x, y] = points.front();
                points.pop();
                for (int i = 0; i < 4; i++) {
                    int nx = x + direction[i][0];
                    int ny = y + direction[i][1];
                    if (nx < 0 || ny < 0 || nx >= m || ny >= n) continue;
                    if (grid[nx][ny] == 2) continue;
                    if (grid[nx][ny] == 1) return step;
                    points.push({nx, ny});
                    grid[nx][ny] = 2;
                }
            }
        
        }
        return 0;
    }
};

判断是否是直角三角形

vector<vector<int>> matrix = {
        {1, 0, 0, 0, 0},
        {0, 1, 0, 0, 1},
        {0, 0, 0, 1, 1},
        {0, 0, 1, 1, 1},
        {0, 1, 1, 1, 1},
        {1, 1, 1, 1, 1}
};

void dfs(vector<vector<int>>& grid, int x, int y, vector<vector<bool>>& visited) {
    if (x < 0 || y < 0 || x >= grid.size() || y >= grid[0].size() || grid[x][y] == 0 || visited[x][y]) return;

    visited[x][y] = true;
    dfs(grid, x-1, y, visited);
    dfs(grid, x+1, y, visited);
    dfs(grid, x, y-1, visited);
    dfs(grid, x, y+1, visited);
}

int main() {
    int m = matrix.size();
    int n = matrix[0].size();
    vector<vector<bool>> visited(m, vector<bool>(n, false));
    vector<vector<int>> dp_x = matrix;
    vector<vector<int>> dp_y = matrix;

    for (int i = 0; i < m; i++) {
        for (int j = 0; j < n; j++) {
            if (matrix[i][j] && i > 0 && j > 0) {
                dp_x[i][j] = dp_x[i][j-1] + matrix[i][j];
                dp_y[i][j] = dp_y[i-1][j] + matrix[i][j];
            }
            cout << "[" << dp_x[i][j] << "," << dp_y[i][j] << "]";
        }
        cout << "\n";
    }
    int count = 0;
    for (int i = 0; i < m; i++) {
        for (int j = 0; j < n; j++) {
            if (matrix[i][j] != 0 && !visited[i][j]) {
                dfs(matrix, i, j, visited);
                count++;
            }
        }
    }

    cout << count;

    return 0;
}

BFS  

 788 · 迷宫II

    int shortestDistance(vector<vector<int>> &maze, vector<int> &start, vector<int> &destination) {
        int m = maze.size(), n = maze[0].size();
        queue<pair<int, int>> q;
        vector<vector<int>> memo(m, vector<int>(n, INT_MAX));
        vector<vector<int>> dirs {{1, 0}, {0, 1}, {0, -1}, {-1, 0}};
        
        q.push({start[0], start[1]});
        memo[start[0]][start[1]] = 0;
        while (!q.empty()) {
            auto pos = q.front();
            q.pop();
            for (auto dir : dirs) {
                int x = pos.first, y = pos.second;
                int dist = memo[x][y];
                while (isValid(maze, x + dir[0], y + dir[1])) {
                    x += dir[0];
                    y += dir[1];
                    dist += 1;
                }
                if (dist < memo[x][y]) {
                    memo[x][y] = dist;
                    q.push({x, y});
                }
            }
        }
        return memo[destination[0]][destination[1]] == INT_MAX ?
                -1 : memo[destination[0]][destination[1]];
    }
    
    bool isValid(vector<vector<int>> &maze, int x, int y) {
        if (x < 0 || y < 0 || x >= maze.size() || y >= maze[0].size()) {
            return false;
        }
        if (maze[x][y] == 1) {
            return false;
        }
        return true;
    }

 127. 单词接龙

    int ladderLength(string beginWord, string endWord, vector<string>& wordList) {
        // 将vector转成unordered_set,提高查询速度
        unordered_set<string> wordSet(wordList.begin(), wordList.end());
        // 如果endWord没有在wordSet出现,直接返回0
        if (wordSet.find(endWord) == wordSet.end()) return 0;
        // 记录word是否访问过
        unordered_map<string, int> visitMap; // <word, 查询到这个word路径长度>
        // 初始化队列
        queue<string> que;
        que.push(beginWord);
        // 初始化visitMap
        visitMap.insert(pair<string, int>(beginWord, 1));

        while(!que.empty()) {
            string word = que.front();
            que.pop();
            int path = visitMap[word]; // 这个word的路径长度
            for (int i = 0; i < word.size(); i++) {
                string newWord = word; // 用一个新单词替换word,因为每次置换一个字母
                for (int j = 0 ; j < 26; j++) {
                    newWord[i] = j + 'a';
                    if (newWord == endWord) return path + 1; // 找到了end,返回path+1
                    // wordSet出现了newWord,并且newWord没有被访问过
                    if (wordSet.find(newWord) != wordSet.end()
                            && visitMap.find(newWord) == visitMap.end()) {
                        // 添加访问信息
                        visitMap.insert(pair<string, int>(newWord, path + 1));
                        que.push(newWord);
                    }
                }
            }
        }
        return 0;
    }

 1293. 网格中的最短路径

    int shortestPath(vector<vector<int>>& grid,int k)
    {
        // seen表示访问到grid中点(x, y)经过的最少障碍物数量
        vector<vector<int>> seen(grid.size(),vector<int>(grid[0].size(),INT_MAX));
        queue<tuple<int,int,int>> q;  // (x, y, o)表示表示到达点(x,y)的障碍物数量为o
        int steps=0;
        q.push({0,0,0});              // 左上角(0,0)障碍物数量为0
        seen[0][0]=0;                 // 到达(0,0)经过的障碍物数量为0
        while(!q.empty()) {
            int size=q.size();
            while(size--) {
                auto [cx,cy,co]=q.front();
                q.pop();
                // bfs到达右下角的值一定是最短路径
                if(cx==(grid.size()-1)&&cy==(grid[0].size()-1))
                    return steps;
                // 上、下、左、右四个方向扩展,得到不同的路径
                for(auto [dr,dc]:vector<pair<int,int>>{{1,0},{0,1},{-1,0},{0,-1}}) {
                    int x=cx+dr;
                    int y=cy+dc;
                    if(x<0||y<0||x>=grid.size()||y>=grid[0].size()) // 判断边界条件
                        continue;
                    // grid[x][y]=1,在新的坐标点(x,y)处,障碍物数量o=co+1.
                    // grid[x][y]=0,在新的坐标点(x,y)处,障碍物数量不变.
                    int o=co+grid[x][y];
                    // 在同一个steps内,这条路径到达点(x,y)的障碍物数量o
                    // 大于等于之前路径障碍物数量seen[x][y],没有任何帮助,则抛弃该路径
                    // 或者障碍物数量大于题目中给定能移除的障碍物数量k,不合法,抛弃该路径
                    if(o>=seen[x][y]||o>k)
                        continue;
                    seen[x][y]=o;     // 到达(x,y)有一个更小的obstacle,更新(x,y)处seen
                    q.push({x,y,o});  // (x,y,o)加入队列
                }
            }
            steps++;
        }
        return -1;
    }

  1926. 迷宫中离入口最近的出口286. 墙与门 1926. 迷宫中离入口最近的出口 

class Solution {
    vector<vector<int>> directions = {{-1, 0}, {1, 0}, {0, 1}, {0, -1}};
    int INF = 2147483647;
public:
    void wallsAndGates(vector<vector<int>>& rooms) {
        queue<pair<int, int>> que;
        int m = rooms.size();
        int n = rooms[0].size();

        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (rooms[i][j] == 0) {
                    que.push({i, j});
                }
            }
        }

        while (!que.empty()) {
            auto obj = que.front();
            que.pop();
            for (int i = 0; i < directions.size(); i++) {
                int x = obj.first + directions[i][0];
                int y = obj.second + directions[i][1];
                if (x < 0 || x >= m || y < 0 || y >= n || rooms[x][y] !=INF) continue;
                rooms[x][y] = rooms[obj.first][obj.second]+1;
                que.push({x, y});
            }
        }

    }   
};

 994. 腐烂的橘子

class Solution {
public:
    vector<vector<int>> directions = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};
    
    int orangesRotting(vector<vector<int>>& grid) {
        queue<pair<int, int>> que;
        vector<vector<int>> dis(grid.size(), vector<int>(grid[0].size(), -1));
        int cnt = 0;
        int n = grid.size(), m = grid[0].size(), ans = 0;
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                if (grid[i][j] == 2) {
                    que.push({i, j});
                    dis[i][j] = 0;
                } else if (grid[i][j] == 1) cnt+=1;
            }
        }

        while (!que.empty()) {
            auto obj = que.front(); que.pop();
            for (int i = 0; i < 4; i++) {
                int nx = obj.first + directions[i][0];
                int ny = obj.second + directions[i][1];

                if (nx < 0 || nx >= n || ny < 0 || ny >= m || ~dis[nx][ny] || !grid[nx][ny]) continue;
                dis[nx][ny] = dis[obj.first][obj.second]+1;
                que.push({nx, ny});
                if (grid[nx][ny] == 1) {
                    cnt -= 1;
                    ans = dis[nx][ny];
                    if (!cnt) break;
                }
            }
        }
        return cnt ? -1 : ans;

    }
};

模拟

224. 基本计算器

class Solution {
private:
    bool isdigit(char c)//判断是否是数字
    {
        return c>='0'&&c<='9';
    }
    int index;
    string S;
public:
    int calcu() {
        stack<int>stk;
        long long num=0;
        char oper='+';//初始符号位为'+'
        while(index<S.size())
        {
            char c=S[index++];
            if(isdigit(c))
                num=num*10+c-'0';//因为num先乘10再加c,如果num类型不是long long,则可能会溢出
                
            if(c=='(')//遇到左括号,开始递归计算
                num=calcu();

            if((!isdigit(c)&&c!=' ')||index>=S.size())//说明遇到了符号位或者到达了字符串的末尾
            {
                int tmp;
                switch(oper)//注意这里参与运算的不是当前遇到的符号位,而是之前放在oper内的符号位!
                {
                    case '+':stk.push(num);break;
                    case '-':stk.push(-num);break;
                    case '*':tmp=stk.top();tmp*=num;stk.pop();stk.push(tmp);break;
                    case '/':tmp=stk.top();tmp/=num;stk.pop();stk.push(tmp);break;
                }
                num=0;
                oper=c;//更新符号位
            }
            if(c==')')//遇到右括号,本轮计算结束
                break;
        }
        int sum=0;
        while(!stk.empty())//求和
        {
            sum+=stk.top();
            stk.pop();
        }
        return sum;
    }

public:
    int calculate(string s) {
        index=0;
        S=s;
        return calcu();
    }
};

 838. 推多米诺

    string pushDominoes(string dominoes) {
        int n = dominoes.size();
        int start = 0, end = 0;
        dominoes = 'L' + dominoes + 'R';

        for (; start < dominoes.size(); start++) {
            if (dominoes[start] != '.') {
                end = start+1;
                while (end < dominoes.size() && dominoes[end] == '.') {
                    end++;
                }
                if (dominoes[start] == 'L' && dominoes[end] == 'L') {
                    for (int i = start; i <= end; i++) {
                        dominoes[i] = 'L';
                    }
                } else if (dominoes[start] == 'R' && dominoes[end] == 'R') {
                    for (int i = start; i <= end; i++) {
                        dominoes[i] = 'R';
                    }
                } else if (dominoes[start] == 'R' && dominoes[end] == 'L') {
                    int r = start;
                    int l = end;
                    while (r < l) {
                        dominoes[r] = 'R';r++;
                        dominoes[l] = 'L';l--;
                    }
                }
                start = end-1;
            }
        }
        return dominoes.substr(1, n);
    }

单调栈

85. 最大矩形 84. 柱状图中最大的矩形 85可求出前缀行最大矩阵,计算最大的矩阵,调用84的函数

b[i]定义为a[i]左边离自己最近的比自己小的数字的下标

    int largestRectangleArea(vector<int>& heights) {
        int n = heights.size();
        if (n == 1) return heights[0];

        stack<int> st;
        int res = 0;
        for (int i = 0; i < n; i++) {
            while (!st.empty() && heights[st.top()] >= heights[i]) {
                int cur = heights[st.top()];
                st.pop();
                int w = i;
                if (!st.empty()) w = i - st.top() - 1;
                res = max(w * cur, res);
            }
            st.push(i);
        }
        while (!st.empty()) {
            int length = heights[st.top()];
            st.pop();
            int weight = n;
            if (!st.empty()) {
                weight = n - st.top() - 1;
            }
            res = max(res, length * weight);
        }
        return res;
    }

 1438. 绝对差不超过限制的最长连续子数组

    int longestSubarray(vector<int>& nums, int limit) {
        deque<int> max_que, min_que;
        int n = nums.size();
        int left = 0, right = 0, res = 0;
        while (right < n) {
            while (!max_que.empty() && max_que.back() < nums[right]) {
                max_que.pop_back();
            }
            while (!min_que.empty() && min_que.back() > nums[right]) {
                min_que.pop_back();
            }
            max_que.push_back(nums[right]);
            min_que.push_back(nums[right]);
            right++;

            while (!max_que.empty() && !min_que.empty() && max_que.front() - min_que.front() > limit) {
                if (max_que.front() == nums[left]) {
                    max_que.pop_front();
                }
                if (min_que.front() == nums[left]) {
                    min_que.pop_front();
                }
                left++;
            }
            res = max(res, right - left);
        }
        return res;
    }

贪心单调栈

 16 .移掉 K 位数字 

给你一个以字符串表示的非负整数 num 和一个整数 k ,移除这个数中的 k 位数字,使得剩下的数字最小

vector<char> stk;
for (auto& digit: num) {
    while (stk.size() > 0 && stk.back() > digit && k) {
        stk.pop_back();
        k -= 1;
    }
    stk.push_back(digit);
}
for (; k > 0; --k) {
    stk.pop_back();
}
string ans = "";
bool isLeadingZero = true;
for (auto& digit: stk) {
    if (isLeadingZero && digit == '0') {
        continue;
    }
    isLeadingZero = false;
    ans += digit;
}
return ans == "" ? "0" : ans;
}

最小堆 

 295. 数据流的中位数

    priority_queue<int, vector<int>, less<int>> maxHeap;
    priority_queue<int, vector<int>, greater<int>> minHeap;
    /** initialize your data structure here. */
    MedianFinder() {

    }
    
    void addNum(int num) {
        if (maxHeap.empty()) {
            maxHeap.push(num);
        } else if (num > maxHeap.top()) {
            minHeap.push(num);
        } else {
            maxHeap.push(num);
        }
        if (minHeap.size() > maxHeap.size()) {
            int rightTop = minHeap.top();
            minHeap.pop();
            maxHeap.push(rightTop);
        } else if (minHeap.size() + 1 < maxHeap.size()) {
            int leftTop = maxHeap.top();
            maxHeap.pop();
            minHeap.push(leftTop);
        }
    }
    
    double findMedian() {
        if (maxHeap.size() > minHeap.size()) {
            return maxHeap.top();
        } else {
            return (maxHeap.top() + minHeap.top()) / 2.0;
        }
    }

扫描线

 218. 天际线问题 遇到拐点进行统计

    vector<vector<int>> getSkyline(vector<vector<int>>& buildings) {
        vector<vector<int>> heights;
        for (int i = 0; i < buildings.size(); i++) {
            heights.push_back({buildings[0], -buildings[2]});
            heights.push_back({buildings[1], buildings[2]});
        }

        sort(heights.begin(), heights.end());
        multiset<int> mulset;

        vector<vector<int>> res;

        int cur_height = 0;
        int pre_height = 0;
        for (pair<int, int> height : heights) {
            if (height.second < 0) {
                mulset.insert(-height.second);
            } else {
                mulset.erase(mulset.find(height.second));
            }

            cur_height = *mulset.rbegin();
            if (cur_height != pre_height) {
                res.push_back({height.first, cur_height});
                pre_height = cur_height;
            }
        }
        return res;
    }

 850. 矩形面积 II 

    const static int MOD = 1e9 + 7;

    int queryWidth(multiset<pair<int, int>>& activate) {
        int res = 0;
        int maxX = -1;
        for (auto [x1, x2] : activate) {
            maxX = max(maxX, x1);
            res += max(0, x2 - maxX);
            maxX = max(maxX, x2);
        }
        return res;
    }

public:
    int rectangleArea(vector<vector<int>>& rectangles) {
        vector<vector<int>> rec;
        for (auto rectangle : rectangles) {
            rec.push_back({rectangle[1], 0, rectangle[0], rectangle[2]});
            rec.push_back({rectangle[3], 1, rectangle[0], rectangle[2]});
        }
        sort(rec.begin(), rec.end());

        int res = 0;
        int lastY = rec[0][0];

        multiset<pair<int, int>> activate;
        for (const vector<int>& rectangle : rec) {
            int y = rectangle[0];
            int state = rectangle[1];
            int x1 = rectangle[2];
            int x2 = rectangle[3];

            res = (res + (long long)queryWidth(activate) * (y - lastY)) % MOD;
            lastY = y;
            if (state == 0) {
                activate.insert({x1, x2});
            } else {
                activate.erase(activate.find({x1, x2}));
            }
        }
        return res;
    }

贪心

  最多可以参加的会议数目

sort(nums.begin(), nums.end(), [](const vector<int>& a, const vector<int>& b) {
    return a[1] < b[1];
});
int min_d = INT_MAX;
int max_d = INT_MIN;
for (const auto& e : nums) {
    min_d = min(min_d, e[0]);
    max_d = max(max_d, e[1]);
}
set<int> s;
for (int i = min_d; i <= max_d; i++) {
    s.insert(i);
}

int ans = 0;
for (const auto& e : nums) {
    auto it = s.lower_bound(e[0]); // 函数用于在指定区域内查找不小于目标值的第一个元素。
    if (it == s.end() || *it > e[1]) continue;
    s.erase(it);
    ans++;
}
return ans;

 1824. 最少侧跳次数

    int minSideJumps(vector<int>& obstacles) {
        int n = obstacles.size();
        int num = 2;
        int res = 0;
        for(int i = 0; i < n-1;){
            //若当前跑道无障碍一直前进
            if(obstacles[i+1] != num){
                i++;
                continue;
            }
            //other和another为另外两条跑道编号
            int other = (num + 1) % 3, another = (num + 2) % 3;
            other = (other == 0)?3:other;
            another = (another == 0)?3:another;
            int t = i;
            //计算测跳道other号跑道时遇到的第一个障碍位置
            while(t < n && obstacles[t] != other){
                t++;
            }
            //计算测跳道another号跑道时遇到的第一个障碍位置
            while(i < n && obstacles[i] != another){
                i++;
            }
            //选择靠后的那条跑道,更新当前跑道序号和当前位置。
            if(t > i){
                num = other;
            }else{
                num = another;
            }
            i = max(t, i) - 1;
            res++;
        }
        return res;
    }

  45. 跳跃游戏 II

    int jump(vector<int>& nums) {
        int cover = 0;
        int cur_cover = 0;
        int times = 0;
        if (nums.size() == 1) return 0;
        for (int i = 0; i < nums.size() - 1; i++) {
            cover = max(i + nums[i], cover);
            if (i == cur_cover) {
                cur_cover = cover;
                times++;
            }
        }
        return times;
    }

 1838. 最高频元素的频数    

    int maxFrequency(vector<int>& nums, int k) {
        sort(nums.begin(), nums.end());
        int n = nums.size();
        long long total = 0;
        int l = 0, res = 1;
        for (int r = 1; r < n; ++r) {
            total += (long long)(nums[r] - nums[r - 1]) * (r - l);
            while (total > k) {
                total -= nums[r] - nums[l];
                ++l;
            }
            res = max(res, r - l + 1);
        }
        return res;
    }

 1423. 可获得的最大点数

int maxScore(vector<int>& cardPoints, int k) {
    //每一步拿牌都是从头或者尾拿, 所以最终剩余的牌一定是连续的, 所以本题换个思路:
    //从一个长度为n的数组中找出一个长度为n-k, 累加值最小的连续子序列[i, j], 那么剩余元素[0, i), (j, n)就是本题需要的解.
    int n = cardPoints.size();
    int windowsSize = n - k;
    int sum = 0;
    for (int i = 0; i < windowsSize; i++) {
        sum += cardPoints[i];
    }
    int all = 0;
    for (auto& cardPoint : cardPoints) {
        all += cardPoint;
    }
    int min_sum = sum;
    for (int i = windowsSize; i < n; i++) {
        sum += cardPoints[i] - cardPoints[i-windowsSize];
        min_sum = min(min_sum, sum);
    }
    return all - min_sum;
}

 528. 按权重随机选择

class Solution {
public:
    int range = 0;
    vector<int> weights;

    Solution(vector<int>& w) {
        for (auto w_ : w) {
            weights.push_back(range);
            range += w_;
        }
    }
    int pickIndex() {
        int rnd = rand() % range;
        int i = 0;

        for (i = 1; i < weights.size(); i++) {
            if (rnd < weights[i]) {
                break;
            }
        }
        return i-1;
    }
};

链接:https://leetcode-cn.com/problems/random-pick-with-weight/solution/wei-rao-li-lun-ke-neng-shi-gong-cheng-zh-fh0r/

递增数

// 889
    string strN = to_string(n);         
    int i = 1;         
    while (i < strN.length() && strN[i - 1] <= strN[i]) {             
        i += 1;         
    }         
    if (i < strN.length()) {             
        while (i > 0 && strN[i - 1] > strN[i]) {                 
            strN[i - 1] -= 1;                 
            i -= 1;             
        }             
        for (i+=1; i < strN.length(); ++i) {                 
            strN[i] = '9';             
        }         
    }         
    return stoi(strN);     
}
// 789
int findIncreaingDigits(long long N) {
    int pos = 0;
    if (N < 10) {
        return N-1;
    }
    string s = to_string(N);
    for(int i  = 1; i < s.size(); i++) {
        if (s[i] - s[i-1] > 1) {
            pos = i;
        } else if (s[i] <= s[i-1]) {
            break;
        }
    }
    string output = "";
    int len = s.size();
    for(int j = 0; j < pos; j++) {
        output += s[j];
    }
    int v = min(9 - (len - pos) + 1, (s[pos]-'0')-1);
    output += (v + '0');
    int start = 9 - (len - pos) + 2;
    for(int j = pos+1; j < len; j++) {
        output += (start + '0');
        start++;
    }
    return stoi(output);
}

386 按字典序输出1-n之间的数

vector<int> lexicalOrder(int n) {
    for (int i = 1; i <= 9; i++) {
        dfs(n, i);
    }
    return ans;
}
void dfs(int n, int num) {
    if (num > n) return;
    ans.push_back(num);
    for (int i = 0; i <= 9; i++) {
        dfs(n, num * 10 + i);
    }
}

投票法

169. 多数元素  

    int majorityElement(vector<int>& nums) {
        int n = nums.size();
        int voted = nums[0];
        int cnt = 1;
        for(int i = 1; i < n; i++) {
            if(nums[i] == voted) {
                cnt++;
            } else {
                cnt--;
            }
            if(cnt == 0) {
                cnt = 1;
                voted = nums[i];
            }
        }

        return voted;
    }

字典序下一个排列

​​​​​​31. 下一个排列   

    void nextPermutation(vector<int>& nums) {
        int i = nums.size() - 2;
        while (i >= 0 && nums[i] >= nums[i + 1]) {
            i--;
        }
        if (i >= 0) {
            int j = nums.size() - 1;
            while (j >= 0 && nums[i] >= nums[j]) {
                j--;
            }
            swap(nums[i], nums[j]);
        }
        reverse(nums.begin() + i + 1, nums.end());
    }

字典序

440. 字典序的第K小数字   

    int findKthNumber(int n, int k) {
        ll cur = 1; //作为一个指针,指向当前所在位置,当p==k时,也就是到了排位第k的数
        ll prefix = 1;//前缀
        while (cur < k) {
            ll cnt = get_count(prefix, n);//获得当前前缀下所有子节点的和
            if (cur + cnt > k) {  //第k个数在当前前缀下
                prefix *= 10; 
                cur++; //把指针指向了第一个子节点的位置,比如11乘10后变成110,指针从11指向了110
            } else if (cur + cnt <= k) {//第k个数不在当前前缀下
                prefix++;
                cur += cnt;//注意这里的操作,把指针指向了下一前缀的起点
            }
        }
        return prefix;
    }

    ll get_count(ll prefix, ll n) {
        ll cnt = 0;
        ll cur = prefix;
        ll next = prefix + 1; //下一个前缀
        for (; cur <= n; cur*=10, next*=10) {
            cnt += min(n+1, next) - cur;
        // 如果说刚刚prefix是1,next是2,那么现在分别变成10和20
        // 1为前缀的子节点增加10个,十叉树增加一层, 变成了两层
        // 如果说现在prefix是10,next是20,那么现在分别变成100和200,
        // 1为前缀的子节点增加100个,十叉树又增加了一层,变成了三层
        }
        return cnt;
    }

智力题

 1224. 最大相等频率

    int maxEqualFreq(vector<int>& nums) {
        /*
        最后一题,考虑四种情况:
1、前缀数组中所有数字的频率只有两种,设为A和B,其中A=B+1,且只有一个数字频率为A;
2、前缀数组中所有数字的频率只有两种,其中只有一个数字的频率为1,其他数字的频率都大于1且相等;
3、整个数组的数字的频率都是1;
4、整个数组都是同一个数字。

对应的处理:
1、把前缀数组中频率为A的数字删去1个即可;
2、把前缀数组中频率为1的数字删去即可;
3、整个数组删去任意一个数字都可;
4、整个数组删去任意一个数字都可。

或许代码中的 fre[maxcnt]==1&&1+fre[maxcnt-1](maxcnt-1)==i+1) 比较难理解,
我们要先知道fre是怎么存储的,例如我们已经遍历了[5,1,1,5,5],
那此时fre[1]=2(包含了1和5),fre[2]=2(包含了1和5),fre[3]=1(只包含5)。
所以上面的 1+fre[maxcnt-1](maxcnt-1)==i+1 表示为 (fre[maxcnt-1]-1)*(maxcnt-1) + (maxcnt)==i+1 即频率为B的数字总数 + 频率为A的数字总数。

        */
        vector<int> cnt(100001,0),fre(100001,0);    //cnt计算数字出现的频率,fre[i]=j表示有j个数字频率为i
        int maxcnt=0,ans=0;
        for(int i=0;i<nums.size();++i){
            int num=nums[i];
            ++cnt[num];
            ++fre[cnt[num]];
            maxcnt=max(maxcnt,cnt[num]);
            //第一、四种情况 || 第二种情况 (i+1就是目前的前缀数组长度)
            if((fre[maxcnt]==1 && 1+fre[maxcnt-1]*(maxcnt-1)==i+1) || (fre[maxcnt]*maxcnt+1==i+1))
                ans=i+1;
        }
        if(maxcnt==1)   //第三种情况
            return nums.size();
        return ans;
    }

判断为树

class Solution {
    bool validTree(int n, int[][] edges) {
        if (edges.length != n-1) return false;
        int[] roots = new int[n];
        for(int i=0; i<n; i++) roots[i] = i;
        for(int i=0; i<edges.length; i++) {
            int root1 = root(roots, edges[i][0]);
            int root2 = root(roots, edges[i][1]);
            if (root1 == root2) return false;
            roots[root2] = root1;
        }
        return true;
    }
    int root(int[] roots, int id) {
        if (id == roots[id]) return id;
        return root(roots, roots[id]);
    }
}

KMP

字符串匹配 A串长n B串长为m 以A每个位置为起点长为m的子串 排序后与B串做精确匹配,问有多少次精确匹配

void getNext(int* next, const string& s) {
    int j = 0;
    next[0] = 0;
    for(int i = 1; i < s.size(); i++) {
        while (j > 0 && s[i] != s[j]) {
            j = next[j - 1];
        }
        if (s[i] == s[j]) {
            j++;
        }
        next[i] = j;
    }
}
int strStr(string haystack, string needle) {
    if (needle.size() == 0) {
        return 0;
    }
    int next[needle.size()];
    getNext(next, needle);
    int j = 0;
    for (int i = 0; i < haystack.size(); i++) {
        while(j > 0 && haystack[i] != needle[j]) {
            j = next[j - 1];
        }
        if (haystack[i] == needle[j]) {
            j++;
        }
        if (j == needle.size() ) {
            return (i - needle.size() + 1);
        }
    }
    return -1;
}

滑窗法 

 76. 最小覆盖子串  

    string minWindow(string s, string t) {
        vector<int> need(128);
        int count = 0;
        for (char ch : t) {
            need[ch]++;
            count++;
        }
        int left = 0; int right = 0; int idx = 0; int size = INT_MAX; int start = 0;
        
        while (right < s.size()) {
            if (need[s[right]] > 0) {
                count--;
            }
            need[s[right]]--;

            if (count == 0) {
                while (left < right && need[s[left]] < 0) {
                    need[s[left++]]++;
                }

                if (right - left + 1 < size) {
                    start = left;
                    size = right - left + 1;
                }

                need[s[left++]]++;
                count++;
            }
            right++;
        }
        return size == INT_MAX ? "" : s.substr(start, size);
    }

​​​​​​通过删除字母匹配到字典里最长单词  

    string findLongestWord(string s, vector<string>& dictionary) {
        string res = "";

        for (string& str : dictionary) {
            if (isSubstr(s, str)) {
                if (str.size() > res.size() || (str.size() == res.size() && str < res)) {
                    res = str;
                }
            }
        }
        return res;
    }

    bool isSubstr(string s, string str) {
        int j = 0; 
        for (int i = 0; i < s.size() && j < str.size(); i++) {
            if (s[i] == str[j]) j++;
        }
        return j == str.size();
    }

340. 至多包含 K 个不同字符的最长子串    

    int lengthOfLongestSubstringKDistinct(string s, int k) {
        if (k == 0) return 0;
        int n = s.size();
        int left = 0;
        int right = 0;
        int ans = 0;
        unordered_map<char, int> dir;

        for (; right < n; right++) {
            dir[s[right]]++;

            while (dir.size() > k) {
                dir[s[left]]--;
                if (dir[s[left]] == 0) dir.erase(s[left]);
                left++;
            }
            ans = max(ans, right - left + 1);
        }
        return ans;
    }

给任意 str,删除任意字符后,求出最长的 4k 长度的 PONY(每个字母 k 长度,如 PPOONNYY)是多长。

    string str = "nnpposoonnansssnwnyasaysy";
    int n = str.size();
    
    string pony = "pony";
    int p = 0;
    int k = INT_MAX;
    int i = 0;
    char pre_char = '0';
    vector<int> frequency(4, 0);
    while (i < n) {
        if (str[i] == pony[p] && pre_char != str[i]) {
            pre_char = pony[p];
            p++;
        }
        if (str[i] == pony[p-1]) {
            frequency[p-1]++;
        }
        i++;
    }
    for (int i = 0; i < frequency.size(); i++) {
        k = min(k, frequency[i]);
    }
    cout << k * 4;

数学题

10. 判断点在三角形内部

struct Point {
    double x;
    double y;
};
double product(Point p1,Point p2,Point p3) {
    //p1p2 向量表示为 (p2.x-p1.x,p2.y-p1.y)
    //p1p3 向量表示为 (p3.x-p1.x,p3.y-p1.y)
    return (p2.x-p1.x)*(p3.y-p1.y) - (p2.y-p1.y)*(p3.x-p1.x);
}
bool isInTriangle(Point p1,Point p2,Point p3,Point o) {
    //保证p1,p2,p3是逆时针顺序
    if(product(p1, p2, p3)<0) return isInTriangle(p1,p3,p2,o);
    if(product(p1, p2, o)>0 && product(p2, p3, o)>0 && product(p3, p1, o)>0)
        return true;
    return false;
}

39. 圆内随机取点

class Solution {
public:
    double rad, xc, yc;
    //c++11 random floating point number generation
    mt19937 rng{random_device{}()};
    uniform_real_distribution<double> uni{0, 1};

    Solution(double radius, double x_center, double y_center) {
        rad = radius, xc = x_center, yc = y_center;
    }

    vector<double> randPoint() {
        double d = rad * sqrt(uni(rng));
        double theta = uni(rng) * (2 * M_PI);
        return {d * cos(theta) + xc, d * sin(theta) + yc};
    }
};

概率题 放球

现在有50个红球,50个蓝球,给小明两个袋子,一个袋子能装任意个球(0~100)。 现由小明将这100个球,以一定方法装入这两个袋子。找另找一个不明真相的路人,闭眼,随机从两个袋子中随机选择一个袋子并摸一个球。要使得他摸出红球的概率最高,小明分配这100个球的最佳方案是____。

取到红球的概率为 0.5*bag1+0.5*bag2 =0.5(bag1+bag2)

当两个袋子中取到红球的概率和最大时,路人摸到红球的概率最高

bag1放一个红球概率 bag1=1

bag2放剩下的球概率 bag2=49/99≈0.5

取到红球的概率约为0.5(1+0.5)=3/4 为概率最大情况

一道机器学习岗位面试题:平均要抛多少次硬币,才能出现连续两次正面向上?

 458. 可怜的小猪

50桶水,其中1桶有毒。有n头猪,一天可以喝两次水,请问在一天内可以找出有毒的那桶水的最小的n

 

    int poorPigs(int buckets, int minutesToDie, int minutesToTest) {
        int states = minutesToTest / minutesToDie + 1;
        return ceil(log(buckets) / log(states));
    }

圆上等概率的选3个点 构成锐角三角形的概率 (现场推导)

  1. 求圆上任取三个点组成锐角三角形的概率(答案:1/4,参考:https://zhuanlan.zhihu.com/p/69530841) (每个锐角三角形,将三个顶点依次和圆心做对称可以映射到三个钝角三角形,说明锐角三角形的概率和钝角三角形的概率是1:3,所以是1/4) (等价为:任取三角形,圆心落在三角形内、外、边上的概率各是多少) 圆上任选三点组成三角形,求三角形的面积的数学期望。 (求面积期望可以分成锐角三角形和钝角三角形两种情况,锐角三角形从圆心将三角形分成三份,面积是三份和;钝角三角形分开后,面积是两份和减去一份差。三角形面积sinα / 2,期望值为1/π,所以结果就是(1/4 * 3 + 3/4) / π = 3/(2π)) 强化版:在一个球面任取4个点,请问这4个点构成的4面体会经过球心的概率。(1/8) (面积法,大三角形面积,等于三个小三角形面积之和,注意浮点数,面积由海伦公式求解) (求直线方程,判断点位于三条直线的什么方向) (矢量叉乘,判断点是否在线段AB的左侧)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值