经典排序
快速排序
#include<iostream>
using namespace std;
const int N=1e6+10;
void quick_sort(int q[],int l,int r){
int i=l-1,j=r+1;
int x=q[l];
if(l>=r)return;//表示左右区间中只有一个数或没有数
while(i<j){
do i++;while(q[i]<x);
do j--;while(q[i]>x);
if(i<j)swap(q[i],q[j]);
}
quick_sort(q,l,j);//quick_sort(q,l,i-1);
quick_sort(q,j+1,r);//quick_sort(q,i,r);
//!!!注意:如果写i 则要考虑边界问题 不要将前面的x定义为q【l】
// 同样取i时需要上取整 i=(l+r+1)>>1;
}
int main()
{
int n;
int a[N];
scanf("%d",&n);
for(int i=0;i<n;i++)scanf("%d",&a[i]);
quick_sort(a,0,n-1);
for(int i=0;i<n;i++)printf("%d ",a[i]);
return 0;
}
快速选择算法
思路: 先用快排将整个序列排好
再比较k与j
再递归到k所在的区间内进行下一步快排
#include<iostream>
using namespace std;
const int N=1e6+10;
void quick_sort(int l,int r,int k){
int i=l-1,j=r+1;
int x=q[l];
if(l>=r)return;//表示左右区间中只有一个数或没有数
while(i<j){
do i++;while(q[i]<x);
do j--;while(q[i]>x);
if(i<j)swap(q[i],q[j]);
}
int sl=j-l+1;
if(k<=sl)return quick_sort(l,j,k);
return quick_sort(j+1,r,k-sl);
}
int main()
{
int n,k;
int a[N];
scanf("%d %d",&n,&k);
for(int i=0;i<n;i++)scanf("%d",&a[i]);
cout<<quick_sort(0,n-1,k)<<endl;
return 0;
}
归并排序
#include<iostream>
using namespace std;
const int N=1e6+10;
int q[N],tmp[N];
void merge_sort(int q[],int l,int r){
int i=l-1,j=r+1;
int mid=l+r>>1;
if(l>=r) return ;
merge_sort(q,l,mid),merge_sort(q,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++];
//重新放回q数组中
for(int i=1,j=0;i<=r;i++,j++)q[i]=tmp[j];
}
}
逆序数的算法
#include<iostream>
using namespace std;
const int N=1e6+10;
int q[N],tmp[N];
typedef long long ll;
ll merge_sort(int l,int r){
int i=l-1,j=r+1;
int mid=l+r>>1;
if(l>=r) return ;
ll res=merge_sort(l,mid)+merge_sort(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++];
res+=mid-i+1;
}
while(i<=mid)tmp[k++]=q[i++];
while(j<=r)tmp[k++]=q[j++];
//重新放回q数组中
for(int i=1,j=0;i<=r;i++,j++)q[i]=tmp[j];
}
return res;
}
二分法
二分查找
数的范围 确定
法一:
//确定边界
int l=0,r=n-1;
while(l<r){
int mid=l+r>>1;
if(q[mid]>=x)r=mid;
else l=mid+1;
}
if(q[l]!=x)cout<<"-1";
//二分一定有解 题目查找不一定有解
法二:
int l=0,r=n-1;
while(l<r){
int mid=l+r>>1;
if(q[mid]<=x)l=mid;
else r=mid-1;
}
经典例题:数的三次方根
#include"bits/stdc++.h"
using namespace std;
double n;
int main()
{
cin>>n;
int l=-10000,r=10000;
while(r-l>1e-8){
double mid= r+l>>1;
if(mid*mid*mid>=n)r=mid;
else l=mid;
}
cout<<l<<endl;
return 0;
}
高精度加法
#include<iostream>
#include<vector>
using namespace std;
const int N=1e6+10;
vector<int> add(vector<int> &a,vector<int> &b)
{
vector<int> c;
int t=0;
for(int i=0;i<a.size()i<b.size();i++){
if(i<a.size())t+=a[i];
if(i<b.size())t+=b[i];
c.push_back(t%10);
t/=10;
}
if(t) c.push_back(1);
}
int main()
{
string s,ss;
vector<int>a,b;
cin>>s>>ss;
for(int i=s.size()-1;i>=0;i--)a.push_back(s[i]-'0');
for(int i=ss.size()-1;i>=0;i--)b.push_back(ss[i]-'0');
auto c=add(a,b);
for(int i=s.size()-1;i>=0;i--)cout<<c[i]<<endl;
}
前缀和
将前n个数值做处理 要求某个区间和能方便快速
#include<iostream>
using namespace std;
const int N=1e5+10;
ll n,m;
ll a[N],b[N];
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++)cin>>a[i];
b[0]=0;
for(int j=1;j<=n;j++)b[i]=b[i-1]+a[i];
while(m--){
int l,r;
cin>>l>>r;
cout<<b[r]-b[l-1]<<endl;
}
}
子矩阵的和
#include<bits/stdc++.h>
using namespace std;
const int N=1010;
int a[N][N];
int s[N][N];
int main()
{
int n,m;
cin>>n>>m>>q;
for(int i=0;i<n;i++){
for(int j=0;j<m;j++)
cin>>a[i][j];
//同时将二维前缀和初始化
s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+a[i][j];
}
while(q--){
int x1,x2,y1,y2;
cin>>x1>>y1>>x2>>y2;
cout<<s[x2][y2]-s[x2][y1-1]-s[x1-1][y2]+s[x1-1][y1-1]<<endl;
}
}
差分
差分和前缀和是逆运算
假设ai=b1+b2+b3+···bi
则称bi为ai的差分
一维
实用价值:
使一段区间内同时进行某一操作 且只需要o(1)的时间
const int N= 100010;
int n,m;
int a[N],b[N];
insert(int l,int r,int c)
{
b[l]+=c;
b[r+1]-=c;
}
int main()
{
scanf("%d",&n,&m);
for(int i =1; i <=n ; i++)
{
scanf("%d",&a[i]);
}
for(int i = 1; i<=n;i++)insert(i,i,a[i]);
while(m--)
{
int l,r,c;
cin>>l>>r>>c;
insert(l,r,c)
}
for(int i = 1; i<= n; i++)b[i]+=b[i-1];
for(int i = 1 ;i <= n;i++)cout<<b[i]<<endl;
}
二维:
差分矩阵
const int N= 100010;
int n,m,q;
int a[N][N],b[N][N];
insert(int x1,int y1,int x2,int y2,int c)
{
b[x1][y1]+=c;
b[x1][y1+1]-=c;
b[x2][y2+1]-=c;
b[x2+1][y2+1]+=c;
}
int main()
{
scanf("%d%d%d",&n,&m,&q);
for(int i =1; i <=n ; i++)
{ for(int j=1;j<=m;j++)
scanf("%d",&a[i]);
}
for(int i = 1; i<=n;i++)
for(int j=1;j<=m;j++)
insert(i,j,i,j,a[i][j]);
while(q--)
{
int x1,y1,x2,y2,c;
cin>>x1>>y1>>x2>>y2>>c;
insert(x1,y1,x2,y2,c);
}
for(int i = 1; i<= n; i++)
for(int j=1;j<=m;j++)
b[i][j]+=b[i-1][j]+b[i][j-1]-b[i-1][j-1];
for(int i = 1 ;i <= n;i++)
for(int j=1; j<=m;j++)
cout<<b[i][j]<<endl;
}
离散化(区间无限大版 差分)
特点 范围很大 具体数值稀疏
//思路:
//将c加入x位置 定义pair
//用二分找到x 再加入c
//询问:前缀和
typedef pair<int,int> PII;
vector<PII> query,add;
vector<int> ans;
int n,m;
int a[N],b[N];
int main()
{
cin>>n>>m;
for(int i=0;i<n;i++)
{
int x,c;
cin>>x>>c;
add.push_back({x,c});
ans.push_back(x);
}
for(int i=0;i<m;i++)
{
int l,r;
cin>>l>>r;
query.push_back({l,r});
ans.push_back(l);
ans.push_back(r);
}
sort(ans.begin(),ans.end());
ans.erase(unique(ans.begin(),ans.end()),ans.end());
//处理插入
for(auto item:add){
int x=find(item.first);
a[x]+=item.second;
}
//预处理前缀和
for(int i=0;i<=ans.size();i++)
{
b[i]=b[i-1]+a[i];
}
//处理询问
for(auto item:query)
{
int l=find(item.first),r=find(item.second);
cout<<s[r]-s[l-1]<<endl;
}
} return 0;
双指针 算法 模板
for(int i=0,j=0;i<=n;i++){
while(i<j && check(i,j))//左右各一个指针且满足某种性质
{
j++;
//每道题的具体逻辑
}
}
一般思路:
先考虑朴素做法 o(n^2)
再优化作法
最长连续不重复子序列
int a[N],s[N];
int main(){
cin>>n;
for(int i=0;i<n;i++)cin>>a[i];
int res=0;
for(int i=0,j=0;i<n;i++){
s[a[i]]++;
while(i>=j&&s[a[i]]>1){
s[a[j]]--;
j++;
}
res=max(res,i-j+1);
}
cout<<res<<endl;
return 0;
}
双指针 另一种题型
先用暴力作法 看是否有单调性
如有 则可将时间复杂度降至一维
判断子序列
概念:子序列 可以不连续地出现在母序列中
同样使用双指针 判断条件:a中的元素是否出现在b中 出现则移动a中的指针 未出现则一直移动b
二进制中1的个数
打印出 某个数的二进制数
int x;
cin>>x;
int res=0;
while(x)x-=lowbit(x),res++;
cout<<res<<endl;
总结
y总的基础算法就到此结束啦!我们下一章节见~~我爱学习算法嘿嘿(づ ̄3 ̄)づ╭❤~