算法题归纳

并查集

class Djset{
public:
    vector<int>r,f;
    Djset(int n):r(n),f(n){iota(f.begin(),f.end(),0);}

    int find(int x)
    {
        if(x!=f[x])f[x]=find(f[x]);
        return f[x];
    }

    void merge(int x,int y)
    {
        int fx=find(x);
        int fy=find(y);
        if(fx==fy)return;
        if(r[fx]>r[fy])f[fy]=fx;
        else
        {
            if(r[fx]==r[fy])r[fy]++;
            f[fx]=fy;
        }
    }

    bool isSame(int x,int y)
    {
        return find(x)==find(y);
    }
};

差分数组

考虑数组 a=[1,3,3,5,8]a=[1,3,3,5,8]a=[1,3,3,5,8],对其中的相邻元素两两作差(右边减左边),得到数组 [2,0,2,3][2,0,2,3][2,0,2,3]。然后在开头补上 a[0]a[0]a[0],得到差分数组
d=[1,2,0,2,3]d=[1,2,0,2,3]d=[1,2,0,2,3]

初始化

//质因子个数
const int MX = 1e5 + 1;
int omega[MX];
int init = []() {
    for (int i = 2; i < MX; i++)
        if (omega[i] == 0) // i 是质数
            for (int j = i; j < MX; j += i)
                omega[j]++; // i 是 j 的一个质因子
    return 0;
}();

单调栈

//739 每日温度 //下一个更大的元素
class Solution {
public:
    vector<int> dailyTemperatures(vector<int>& temperatures) {
        int n=temperatures.size();
        vector<int>ans(n);
        stack<int>stk;
        for(int i=0;i<n;i++)
        {
            while(!stk.empty()&&temperatures[i]>temperatures[stk.top()])
            {
                ans[stk.top()]=i-stk.top();
                stk.pop();
            }
            stk.push(i);
        }
        return ans;
    }
};
//2104子数组范围和//前一个更大的元素和后一个更大的元素
        vector<int>rmax(n,n),lmax(n,-1);
        for(int i=0;i<n;i++)
        {
            while(!mx.empty()&&nums[i]>nums[mx.top()])
            {
                rmax[mx.top()]=i;
                mx.pop();
            }
            if(!mx.empty())lmax[i]=mx.top();
            mx.push(i);
        }

二叉树

//二叉树的最近公共祖先
class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if(root==p||root==q||!root)return root;
        TreeNode* left=lowestCommonAncestor(root->left,p,q);
        TreeNode* right=lowestCommonAncestor(root->right,p,q);
        if(left&&right)return root;
        if(!left&&right)return right;
        if(left&&!right)return left;
        return nullptr;
    }
};

//最深叶节点的最近公共祖先
class Solution {
public:
    pair<TreeNode*, int> f(TreeNode* root) {
        if (!root) return {root, 0};
        auto left = f(root->left);
        auto right = f(root->right);
        if (left.second > right.second) return {left.first, left.second + 1};
        if (left.second < right.second) return {right.first, right.second + 1};
        return {root, left.second + 1};
    }
    TreeNode* lcaDeepestLeaves(TreeNode* root) {
        return f(root).first;
    }
};

二维前缀和

class MatrixSum {
private:
    vector<vector<int>> sum;

public:
    MatrixSum(vector<vector<int>> &matrix) {
        int m = matrix.size(), n = matrix[0].size();
        // 注意:如果 matrix[i][j] 范围很大,需要使用 long long
        sum = vector<vector<int>>(m + 1, vector<int>(n + 1));
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                sum[i + 1][j + 1] = sum[i + 1][j] + sum[i][j + 1] - sum[i][j] + matrix[i][j];
            }
        }
    }

    // 返回左上角在 (r1,c1) 右下角在 (r2-1,c2-1) 的子矩阵元素和(类似前缀和的左闭右开)
    int query(int r1, int c1, int r2, int c2) {
        return sum[r2][c2] - sum[r2][c1] - sum[r1][c2] + sum[r1][c1];
    }

    // 如果你不习惯左闭右开,也可以这样写
    // 返回左上角在 (r1,c1) 右下角在 (r2,c2) 的子矩阵元素和
    int query2(int r1, int c1, int r2, int c2) {
        return sum[r2 + 1][c2 + 1] - sum[r2 + 1][c1] - sum[r1][c2 + 1] + sum[r1][c1];
    }
};

回溯

//全排列
class Solution {
private:
    vector<vector<int>>ans;
    vector<int>path;
    void dfs(vector<int>& nums,vector<bool>& used)
    {
        if(path.size()==nums.size())
        {
            ans.push_back(path);
            return;
        }
        for(int i=0;i<nums.size();i++)
        {
            if(used[i])continue;
            used[i]=true;
            path.push_back(nums[i]);
            dfs(nums,used);
            path.pop_back();
            used[i]=false;
        }
    }

public:
    vector<vector<int>> permute(vector<int>& nums) {
        vector<bool> used(nums.size(),false);
        dfs(nums,used);
        return ans;
    }
};

//子集
class Solution {
private:
    vector<vector<int>> result;
    vector<int> path;
    void backtracking(vector<int>& nums, int startIndex) {
        result.push_back(path); // 收集子集,要放在终止添加的上面,否则会漏掉自己
        if (startIndex >= nums.size()) { // 终止条件可以不加
            return;
        }
        for (int i = startIndex; i < nums.size(); i++) {
            path.push_back(nums[i]);
            backtracking(nums, i + 1);
            path.pop_back();
        }
    }
public:
    vector<vector<int>> subsets(vector<int>& nums) {
        result.clear();
        path.clear();
        backtracking(nums, 0);
        return result;
    }
};

回文

//整个字符串s是否是回文//双指针
    bool isPal(string& s)
    {
        for(int i=0,j=s.size()-1;i<j;i++,j--)
        {
            if(s[i]!=s[j])return false;
        }
        return true;
    }


//字符串s从i到j的部分是否是回文//动态规划
        int n = s.size();
        vector<vector<int>> g(n, vector<int>(n, true));
        for(int i=n-1;i>=0;i--)
        {
            for(int j=i+1;j<n;j++)
            {
                g[i][j]=(s[i]==s[j])&&g[i+1][j-1];
            }
        }
//回文字符串分割成若干回文字符串的最小次数
    int minCut(string s) {
        int n = s.size();
        vector<vector<int>> g(n, vector<int>(n, true));
        for(int i=n-1;i>=0;i--)
        {
            for(int j=i+1;j<n;j++)
            {
                g[i][j]=(s[i]==s[j])&&g[i+1][j-1];
            }
        }

        vector<int> f(n, INT_MAX);
        for(int i=0;i<n;i++)
        {
            if(g[0][i])f[i]=0;
            else
            {
                for(int j=0;j<i;j++)
                {
                    if(g[j+1][i])f[i]=min(f[i],f[j]+1);
                }
            }
        }
        return f[n-1];
    }


//判断回文数
    bool isPalindrome(int x) {
        if (x < 0 || (x % 10 == 0 && x != 0)) {
            return false;
        }
        int revertedNumber = 0;
        while (x > revertedNumber) {
            revertedNumber = revertedNumber * 10 + x % 10;
            x /= 10;
        }
        return x == revertedNumber || x == revertedNumber / 10;
    }

//构造l~r范围内的回文数
        vector<long long>pal;
        for (int i = 1; i < 100000; ++i)
        {
            string s = to_string(i);
            string s2 = s;
            reverse(s2.begin(), s2.end());
            long m= stol(s + s2);
            if (m > r)break;
            else if (m >= l)pal.push_back(m);
        }
      for (int i = 1; i < 100000; ++i)
        {
            string s = to_string(i);
            string s2 = s;
            reverse(s2.begin(), s2.end());
            long m= stol(s.append(s2.begin()+1, s2.end()));
            if (m > r)break;
            else if (m >= l)pal.push_back(m);
        }

珂朵莉树

class ChthollyTree{
private:
    struct ChthollyNode{
        int l,r,v;
        ChthollyNode(int l,int r,int v):l(l),r(r),v(v){}
        bool operator<(const ChthollyNode &o)const {return l<o.l;}
    };
    set<ChthollyNode>tree;
public:
    ChthollyTree() {}
    set<ChthollyNode>::iterator split(int p)
    {
        auto it=tree.lower_bound(ChthollyNode(p,0,0));
        if(it!=tree.end()&&it->l==p)return it;
        it--;
        int l=it->l,r=it->r,v=it->v;
        tree.erase(it);
        tree.insert(ChthollyNode(l,p-1,v));
        return tree.insert(ChthollyNode(p,r,v)).first;
    }

    void assign(int l,int r,int v)
    {
        auto it_r=split(r+1);
        auto it_l=split(l);
        tree.erase(it_l,it_r);
        tree.insert(ChthollyNode(l,r,v));
    }

    bool check(int l, int r) {
        auto it_r=split(r+1);
        auto it_l=split(l);
        auto it=it_l;
        for(;it!=it_r;it++)if(it->v==0)return false;
        return true;
    }
    
    void insert(int l,int r,int v)
    {
        tree.insert(ChthollyNode(l,r,v));
    }
};

快速幂

int qmi(int a, int b, int p) {
     int ans = 1;
     while(b) {
         if(b & 1) ans = ans * a % p;
         a = a * a % p;
        b = b >> 1;
    }
     return ans;
 }

链表

tips
1.要把最后的出口封住,避免链表循环

//反转链表
//反转全部
class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        ListNode *temp;
        ListNode *left=NULL;
        ListNode *right=head;
        while(right)
        {
            temp=right->next;
            right->next=left;
            left=right;
            right=temp;
        }
        return left;
    }
};

//反转链表
//从第left个节点反转到第right个节点
class Solution {
public:
    ListNode *reverseBetween(ListNode *head, int left, int right) {
        // 设置 dummyNode 是这一类问题的一般做法
        ListNode* dummyNode = new ListNode(-1);
        dummyNode->next = head;
        ListNode* start = dummyNode;
        for (int i=0;i<left-1;i++)start=start->next;
        ListNode* cur = start->next;
        ListNode* end=cur;
        ListNode* nxt=cur->next;
        ListNode* tmp;
        for (int i = 0; i < right - left; i++) {
            tmp = nxt->next;
            nxt->next=cur;
            cur=nxt;
            nxt=tmp;
        }
        start->next=cur;
        end->next=nxt;
        return dummyNode->next;
    }
};

//k个一组反转链表
class Solution {
public:
    vector<ListNode *>reverseBetween(ListNode*head,int k) {
        // 设置 dummyNode 是这一类问题的一般做法
        ListNode* dummyNode = new ListNode(-1);
        dummyNode->next = head;
        ListNode* start = dummyNode;
        ListNode* cur = start->next;
        ListNode* end=cur;
        ListNode* nxt=cur->next;
        ListNode* tmp;
        for (int i=0;i<k-1;i++) {
            tmp = nxt->next;
            nxt->next=cur;
            cur=nxt;
            nxt=tmp;
        }
        start->next=cur;
        end->next=nxt;
        return {dummyNode->next,end};
    }
    ListNode* reverseKGroup(ListNode* head, int k) {
        ListNode* cur=head;
        int cnt=1;
        while(cur->next)
        {
            cnt++;
            cur=cur->next;
        }

        vector<ListNode*>ans;
        ListNode* start=head;
        ListNode* end=head;

        ListNode* dummyNode=new ListNode(-1);
        dummyNode->next=head;
        cur=dummyNode;

        while(cnt>=k)
        {
            cnt-=k;
            ans=reverseBetween(start,k);
            cur->next=ans[0];
            cur=ans[1];
            start=ans[1]->next;
        }
        return dummyNode->next;
    }
};

//相交链表找第一个交点
class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        if (headA == nullptr || headB == nullptr) {
            return nullptr;
        }
        ListNode *pA = headA, *pB = headB;
        while (pA != pB) {
            pA = pA == nullptr ? headB : pA->next;
            pB = pB == nullptr ? headA : pB->next;
        }
        return pA;
    }
};


//删除节点(但不知道头节点)
class Solution {
public:
    void deleteNode(ListNode* node) {
        node->val = node->next->val;
        node->next = node->next->next;
    }
};

//循环链表 找起点
class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        ListNode *slow = head, *fast = head;
        while (fast != nullptr) {
            slow = slow->next;
            if (fast->next == nullptr) {
                return nullptr;
            }
            fast = fast->next->next;
            if (fast == slow) {
                ListNode *ptr = head;
                while (ptr != slow) {
                    ptr = ptr->next;
                    slow = slow->next;
                }
                return ptr;
            }
        }
        return nullptr;
    }
};

//两数相加
class Solution {
public:
    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
        stack<int> s1, s2;
        while (l1) {
            s1.push(l1 -> val);
            l1 = l1 -> next;
        }
        while (l2) {
            s2.push(l2 -> val);
            l2 = l2 -> next;
        }
        int carry = 0;
        ListNode* ans = nullptr;
        while (!s1.empty() or !s2.empty() or carry != 0) {
            int a = s1.empty() ? 0 : s1.top();
            int b = s2.empty() ? 0 : s2.top();
            if (!s1.empty()) s1.pop();
            if (!s2.empty()) s2.pop();
            int cur = a + b + carry;
            carry = cur / 10;
            cur %= 10;
            ListNode* curnode = new ListNode(cur);
            curnode -> next = ans;
            ans = curnode;
        }
        return ans;
    }
};

前缀和

//前缀和+map//1124
class Solution {
public:
    int longestWPI(vector<int>& hours) {
        int n=hours.size();
        unordered_map<int,int>mp;
        int ans=0,s=0;
        for(int i=0;i<n;i++)
        {
            s+=hours[i]>8?1:-1;
            if(s>0)ans=max(ans,i+1);
            else if(mp.count(s-1))ans=max(ans,i-mp[s-1]);
            if(!mp.count(s))mp[s]=i;
        }
        return ans;
    }
};
//前缀和+二分查找//1208
class Solution {
public:
    int equalSubstring(string s, string t, int maxCost) {
        int n=s.size();
        vector<int>sum;
        sum.push_back(0);
        int pre=0,ans=0;
        for(int i=0;i<n;i++)
        {
            pre+=abs(t[i]-s[i]);
            int target=pre-maxCost;
            int idx=lower_bound(sum.begin(),sum.end(),target)-sum.begin();
            if(idx<sum.size())ans=max(ans,i-idx+1);
            sum.push_back(pre);
        }
        return ans;
    }
};

拓扑排序

//课程表//207//1462
class Solution {
public:
    vector<bool> checkIfPrerequisite(int numCourses, vector<vector<int>>& prerequisites, vector<vector<int>>& queries) {
        vector<vector<int>> g(numCourses);
        vector<int> indgree(numCourses);
        vector<vector<bool>>isPre(numCourses,vector<bool>(numCourses,false));
        for(auto&p:prerequisites)
        {
            indgree[p[1]]++;
            g[p[0]].push_back(p[1]);
        }
        queue<int>q;
        for(int i=0;i<numCourses;i++)
        {
            if(indgree[i]==0)q.push(i);
        }
        while(!q.empty()) {
            int cur=q.front();
            q.pop();
            for(auto& ne:g[cur])
            {
                isPre[cur][ne]=true;
                for(int i=0;i<numCourses;i++)
                {
                    isPre[i][ne]=isPre[i][ne]||isPre[i][cur];
                }
                indgree[ne]--;
                if(indgree[ne]==0)q.push(ne);
            }
        }
        vector<bool>res;
        for(auto& query:queries)res.push_back(isPre[query[0]][query[1]]);
        return res;
    }
};


//计算树枝的长度
        function<void(int, int)> rdfs = [&](int x, int depth) {

            ans[x] = depth;

            for (int y: rg[x]) {

                if (deg[y] == 0) { // 树枝上的点在拓扑排序后,入度均为 0

                    rdfs(y, depth + 1);

                }

            }

位运算库函数

(a&b) ^ (a&c)=a&(b^c)

__builtin_clz( ) / __builtin_clzll
返回括号内数的二进制表示数前导0的个数

__builtin_ctz( ) / __buitlin_ctzll( )

用法:返回括号内数的二进制表示数末尾0的个数

优先队列

//堆顶是最小值
priority_queue<int>pq;
priority_queue<pair<int, int>, vector<pair<int,int>>, greater<pair<int,int>>> q;


auto cmp = [](const ListNode *a, const ListNode *b) {
            return a->val > b->val; // 最小堆
        };
        priority_queue<ListNode*, vector<ListNode*>, decltype(cmp)> pq;

字典树

class Trie{
public:
    Trie* next[2];
    int cnt=0;

    Trie():next{nullptr,nullptr},cnt(0){};

    void insert(int x)
    {
        Trie* node=this;
        for(int i=19;i>=0;i--)
        {
            int v=x>>i&1;
            if(!node->next[v])node->next[v]=new Trie();
            node=node->next[v];
            node->cnt++;
        }
    }

    void remove(int x)
    {
        Trie* node=this;
        for(int i=19;i>=0;i--)
        {
            int v=x>>i&1;
            node=node->next[v];
            node->cnt--;
        }
    }

    int getXor(int x)
    {
        Trie* node=this;
        int ans=0;
        for(int i=19;i>=0;i--)
        {
            int v=x>>i&1;
            if(node->next[v^1]&&node->next[v^1]->cnt)
            {
                node=node->next[v^1];
                ans+=1<<i;
            }
            else node=node->next[v];
        }
        return ans;
    }
       
        int getXor(int x,int high)//异或结果小于high的数量
    {
        Trie* node=this;
        int sum=0;
        for(int i=14;i>=0;i--)
        {
            int v=x>>i&1;
            int mx=high>>i&1;
            if(mx)
            {
                if(node->next[v])sum+=node->next[v]->cnt;
                if(!node->next[v^1])return sum;
                node=node->next[v^1];
            }
            else
            {
                if(!node->next[v])return sum;
                node=node->next[v];
            }
        }
        sum+=node->cnt;
        return sum;
    }
};

字符串处理

//去掉逗号
    vector<string> split(const string &str, char dec) {
        int pos = 0;
        int start = 0;
        vector<string> res;
        while (pos < str.size()) {
            while (pos < str.size() && str[pos] == dec) {
                pos++;
            }
            start = pos;
            while (pos < str.size() && str[pos] != dec) {
                pos++;
            }
            if (start < str.size()) {
                res.emplace_back(str.substr(start, pos - start));
            }
        }
        return res;
    }

//去掉空格
    vector<string> split(string& s, char delim)
    {
        stringstream ss(s);
        string item;
        vector<string> res;
        while(getline(ss,item,delim))res.emplace_back(item);
        return res;
    }

DP

背包问题

//01背包
//容量固定的背包最多能装多少价值的物品
    vector<int> dp(bagWeight + 1, 0);
    for(int i = 0; i < weight.size(); i++) { // 遍历物品
        for(int j = bagWeight; j >= weight[i]; j--) { // 遍历背包容量
            dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
        }
    }


//有多少种方法可以装满固定容量的背包
vector<int> dp(bagSize + 1, 0);
        dp[0] = 1;
        for (int i = 0; i < nums.size(); i++) {
            for (int j = bagSize; j >= nums[i]; j--) {
                dp[j] += dp[j - nums[i]];
            }
        }


//完全背包
//容量固定的背包最多能装多少价值的物品
    for(int i = 0; i < weight.size(); i++) { // 遍历物品
        for(int j = weight[i]; j <= bagWeight; j++) { // 遍历背包容量
            dp[j] = max(dp[j], dp[j - weight[i]] + value[i]);
        }
    }

//有多少种方法可以装满固定容量的背包(不同顺序不一样)
        for (int i = 0; i <= target; i++) { // 遍历背包
            for (int j = 0; j < nums.size(); j++) { // 遍历物品
                if (i - nums[j] >= 0 && dp[i] < INT_MAX - dp[i - nums[j]]) {
                    dp[i] += dp[i - nums[j]];
                }
            }

股票问题

1)买卖股票含手续费

class Solution {
public:
    int maxProfit(vector<int>& prices, int fee) {
        int n=prices.size();
        vector<vector<int>>DP(n,vector<int>(2,0));
        DP[0][1]=-prices[0];
        for(int i=1;i<n;i++)
        {
            DP[i][0]=max(DP[i-1][0],DP[i-1][1]+prices[i]-fee);
            DP[i][1]=max(DP[i-1][1],DP[i-1][0]-prices[i]);
        }
        return DP[n-1][0];
    }
};


2)买卖股票含冷冻期
class Solution {
public:
int maxProfit(vector<int>& prices) {
       if(prices.size()==1)return 0;
       int n=prices.size();
       vector<vector<int>> dp(n,vector<int>(2));
       dp[0][0]=-prices[0];
       dp[1][0]=max(-prices[0],-prices[1]);
       dp[1][1]=max(0,prices[1]-prices[0]);
       for(int i=2;i<prices.size();i++)
       {
           dp[i][0]=max(dp[i-1][0],dp[i-2][1]-prices[i]);
           dp[i][1]=max(dp[i-1][1],dp[i-1][0]+prices[i]);
       }
       return dp[n-1][1];
   }
};

3)买卖股票限定交易次数
class Solution {
public:
    int maxProfit(int k, vector<int>& prices) {
        vector<int>buy(k+1,-prices[0]);
        vector<int>sell(k+1,0);
        for(int i=1;i<prices.size();i++)
        {
            for(int j=1;j<=k;j++)
            {
                buy[j]=max(buy[j],sell[j-1]-prices[i]);
                sell[j]=max(sell[j],buy[j]+prices[i]);
            }
        }
        return sell[k];
    }
};

图论

无向树

//计算节点一端连接点的总和
class Solution {
public:
    int ans=0;
    long long dfs(int x,int fa,vector<vector<int>>&g,int k,vector<int>&values)
    {
        long long sum=values[x];
        for(auto y:g[x])if(y!=fa)sum+=dfs(y,x,g,k,values);
        ans+=(sum%k==0);
        return sum;
    }
    int maxKDivisibleComponents(int n, vector<vector<int>>& edges, vector<int>& values, int k) {
        vector<vector<int>>g(n);
        for(auto &e:edges)
        {
            int x=e[0];
            int y=e[1];
            g[x].push_back(y);
            g[y].push_back(x);
        }
        dfs(0,-1,g,k,values);
        return ans;
    }
};

有向图

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值