并查集
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;
}
};