1.快速排序(Acwing 785.快速排序)
void quick_sort(int a[],int l,int r)
{
if(l>=r)return;
int i=l-1,j=r+1,x=l+r>>1;//'+'的优先级高于'>>'移位运算符
while(i<j)
{
do i++;while(a[i]<x);
do j--;while(a[j]>x);
if(i<j) swap(a[i],a[j]);
}
quick_sort(q,l,j);
quick_sort(q,j+1,r);
}
解析:
1.首先设立一个X将数组分开成两个数组;
2.设立两个指针i和j分别从首尾两端向彼此靠近,将小于X的数据都放在X的左边,大于X的数据放在X的右边。当两个指针相遇时,继续下一步;
3.将左边的数据和右边的数据继续进行上面的操作;
4.重复上述过程,可以看出这是一个递归的过程;当左右两边的数据都排好序之后,整个数组就自然有序了。
2.归并排序 Acwing 787.归并排序
void merge_sort(int a[],int l,int r)
{
if(l>=r)return;
int mid=l+r>>1;
merge_sort(a,l,mid);
merge_sort(a,mid+1,r);
int k=0,i=l,j=mid+1;
while(i<=mid&&j<=r)
{
if(q[i]<q[j])tmp[k++]=q[i++];
else
tmp[k++]=q[j--];
}
while(i<=mid)tmp[k++]=q[i++];
while(j<=r)tmp[k++]=q[j--];
for(int i=l,k=0;i<=r;i++)
q[k++]=tmp[i++];
}
3.整数二分算法模板 Acwing 789.数的范围
bool check(int x)
{
/* ... */
}//检查x是否满足某种性质
//区间[l,r]被划分成[l,mid]和[mid+1,r]时使用
int bsearsh_1(int l,int r)
{
while(l<r){
int mid=l+r>>1;
if(check(mid))r=mid;
else l=mid+1;
}
return l;
}
//区间[l,r]被划分成[l,mid-1]和[mid,r]时使用
int bsearsh_2(int l,int r)
{
while(l<r){
int mid=l+r+1>>1;
if(check(mid))l=mid;
r=mid-1;
}
return l;
}
4.浮点数二分模板 Acwing 790.数的三次方根
bool check(int x)
{
/* */
}//检查x是否服从某种性质
double bsearsh(double l,double r)
{
const double e=1e-6;
while(l<r)
{
double mid=(l+r)/2;
if(check(mid)) r=mid;
else l=mid;
}
return l;
}
5.高精度加法 Awcing 791.高精度加法
//C=A+B,A>=0,B>=0
vector<int>add(vector<int> &A,vector<int> &B)
{
if(A.size()<B.size()) return add(B,A);
int t=0;
vector<int> C;
for(int i=0;i<A.size();i++)
{
t+=A[i];
if(i<B.size())t+=B[i];
C.push_back(t%10);
t/=10;
}
if(t)C.push_back(t);
return C;
}
6.高精度减法 Acwing 792.高精度减法
//C=A-B,A>=B,A>=0,B>=0
vector<int>sub(vector<int> &A,vector<int> &B)
{
if(A.size()<B.size()) return sub(B,A);
vector<int> C;
for(int i=0,t=0;i<A.size();i++)
{
t=A[i]-t;
if(i<B.size())t-=B[i];
C.push_back(t+10%10);
if(t<0) t=1;
else t=0;
}
while(C.size()>1&&C.back()==0)C.pop_back();//去除前导0
return C;
}
7.高精度乘低精度 Acwing 793.高精度乘法
//C=A*b,A>0,b>0
vector<int>mul(vector<int> &A,int b)
{
vector<int> C;
int t=0;
for(int i=0;i<A.size()||t;i++)
{
if(i<A.size())t+=b*A[i];
C.push_back(t%10);
t/=10;
}
return C;
}
8.高精度除以低精度 Acwing 794.高精度除法
//C=A*b,A>0,b>0
vector<int>div(vector<int> &A,int b)
{
vector<int> C;
int r=0;
for(int i=A.size()-1;i>=0;i--)
{
r=r*10+A[i];
C.push_back(r/b);
r%=b;
}
reverse(C.begin(),C.end());
while(C.size()>1&&C.back()==0)C.pop_back();
return C;
}
9.一维前缀和 Acwing 795.前缀和
S[i]=a[1]+a[2]+...a[i]
a[l]+...+a[r]=S[r]-S[l-1]
10.二维前缀和 Acwing 796.子矩阵之和
S[i,j]=第i行j列格子左上部分所有元素的和
以(x1,y1)为左上角,(x2,y2)为右下角的子矩阵之和为:
S[x2,y2]-S[x1-1,y1-1]+S[x2,y1-1]+S[x1-1,y1-1]
11.一维差分 Acwing 797.差分
给区间[l,r]中的每个数加上c:B[l]+=c,B[r+1]-=c
12.二维差分 Acwing 798.差分矩阵
给定(x1,y1)为左上角,(x2,y2)为右下角的子矩阵中的所有元素加上c:
S[x1,y1]+=c,S[x2+1,y1]-=c,S[x1,y2+1]-=c,S[x2+1,y2+1]+=c
13.位运算 Acwing 801.二进制中1的个数
求n的第k为数字:n>>k&1
返回n的最后一位1:lowbit(n)=n&-n
14.双指针算法 Acwing 799.最长连续不重复子序列,Acwing 800.数组元素的目标和
for(int i=0,j=0;i<n;i++)
{
while(j<i&&check(i,j)) j++;
//具体问题的逻辑
}
常见分类问题:
(1)对于一个序列,用两个指针维护一段区间
(2)对于两个序列,维护某种次序,比如归并排序中合并两个有序序列的操作
15.离散化 Acwing 802.区间和
vector<int> alls;//存储所有待离散化的值
sort(alls.begin(),alls.end());//将所有值排序
alls.erase(unique(alls.begin(),alls.end()));//去掉重复元素
//二分求出x对应离散化的值
int find(int x)//找到第一个大于等于x位置
{
int l=0,r=all.size()-1;
while(l<r)
{
int mid=l+r>>1;
if(alls[mid]>=x)r=mid;
else l=mid+1;
}
return r+1;//映射到1,2,...n
}
16.区间合并 Acwing 803.区间合并
//将所有存在交集的区间合并
typedef pair<int, int> PII;
void merge(vector<PII> &segs)
{
vector<PII> res;
sort(segs.begin(),segs.end());
int st=-2e9,ed=-2e9;
for(auto seg:segs)
if(ed<seg.first)
{
if(st!=-2e9) res.push_back({st,ed});
st=seg.first,seg.second;
}
else
ed=max(ed,seg.second);
if(st!=-2e9)res.push_back({st,ed});
segs=res;
}