排序,二分,高精度,前缀和差分,双指针,位运算,离散化,区间合并(算法基础课)


目录

1快速排序

#include<iostream>
#include<cstdio>
using namespace std;
const int N = 100010;
int a[N];
void qs(int a[], int l, int r){
    if(l>=r) return ;
    int i = l - 1,j=r+1,x=a[(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]);
    }
    qs(a,l,j);
    qs(a,j+1,r);
}
int main(){
    int n;
    scanf("%d", &n);
    for(int i = 0; i < n; i ++) scanf("%d",&a[i]);
    qs(a,0,n-1);
    for(int i = 0; i < n; i ++) printf("%d ", a[i]);
    return 0;
}

在这里插入图片描述为啥选取J,有证明可以证明,为i的话不能保证被正确划分,只有J才行。

2第k小数

用快速排序选择第k个,进行分治
通过长度和K比较再分治,

#include<iostream>
#include<cstdio>
using namespace std;
const int N = 100010;
int a[N];
int qs(int a[], int l, int r,int k){
    if(l>=r) return a[l];
    int i = l - 1,j=r+1,x=a[(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]);
    }
    if(j - l + 1 >= k) return qs(a,l,j,k); //左边
    else return qs(a, j+1, r, k - (j - l + 1));  //右边,变为第k - (j – l + 1)个
}
int main(){
    int n,k;
    scanf("%d %d", &n,&k);
    for(int i = 0; i < n; i ++) scanf("%d",&a[i]);
    cout<<qs(a,0,n-1,k);
    return 0;
}

3归并排序:

结束:将区间二等分,当只有两个元素时,分了之后就直接返回了,然后将两个进行排序合并,依次递归回去。Tmp暂存,需要将值返回给原数组

#include <iostream>
using namespace std;
const int N = 1e6 + 10;
int a[N], tmp[N];
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(a[i]<=a[j]) tmp[k++]=a[i++];
        else tmp[k++]=a[j++];
    }
    while(i<=mid) tmp[k++]=a[i++];
    while(j<=r) tmp[k++]=a[j++];
    for(i =l ,j=0;i<=r;i++,j++) a[i]=tmp[j];
    
}
int main()
{
    int n;
    scanf("%d", &n);
    for (int i = 0; i < n; i ++ ) scanf("%d", &a[i]);
    merge_sort(a, 0, n - 1);
    for (int i = 0; i < n; i ++ ) printf("%d ", a[i]);
    return 0;
}

4求逆序对个数:

注意使用Long, long。

#include <iostream>
using namespace std;
const int N = 1e6 + 10;
int a[N], tmp[N];
long long ans=0;
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(a[i]<=a[j]) tmp[k++]=a[i++];
        else {
            tmp[k++]=a[j++];
            ans = ans + (mid - i + 1);
        } 
    }
    while(i<=mid) tmp[k++]=a[i++];
    while(j<=r) tmp[k++]=a[j++];
    for(i =l,j=0;i<=r;i++,j++) a[i]=tmp[j];
}
int main()
{
    int n;
    scanf("%d", &n);
    for (int i = 0; i < n; i ++ ) scanf("%d", &a[i]);
    merge_sort(a, 0, n - 1);
    cout<<ans;
    return 0;
}

5在数组中查找某元素,找不到就输出-1,找到了就输出不小于该元素的最小位置和不大于该元素的最大位置。

#include <iostream>
using namespace std;
const int N = 100010;
int n, m;
int q[N];
int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 0; i < n; i ++ ) scanf("%d", &q[i]);
    while (m -- )
    {
        int x;
        scanf("%d", &x);
        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 -1" << endl;
        else
        {
            cout << l << ' ';
            int l = 0, r = n - 1;
            while (l < r)
            {
                int mid = l + r + 1 >> 1;
                if (q[mid] <= x) l = mid;
                else r = mid - 1;
            }
            cout << l << endl;
        }
    }
    return 0;
}

1数的三次方根

给定一个浮点数 n,求它的三次方根。

#include<iostream>
using namespace std;
int main(){
    double n;
    cin>>n;
    const double esp = 1e-8;
    double l = -10000, r = 10000;
    while( r - l >= esp){   //浮点数这样写
        double mid = (l+r)/2;
        if(mid*mid*mid>=n) r = mid;
        else l = mid;
    }
    printf("%.6f",l);
    return 0;
}

6前缀和:

#include<iostream>
using namespace std;
const int N = 1e5+10;
int a[N],sum[N];
int main(){
    int n,m,x;
    cin>>n>>m;
    for(int i = 1;i<=n;i++){
        cin>>x;
        sum[i]=x+sum[i-1];
    }
    while(m--){
        int l,r;
        cin>>l>>r;
        cout<<sum[r]-sum[l-1]<<endl;
    }
    return 0;
}

7矩阵的子矩阵和:

#include<iostream>
#include<cstdio>
using namespace std;
const int N=1010;
int n,m,q;
int a[N][N],s[N][N];
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][j]);
        }
    }
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=m;j++)
        {
            s[i][j]=s[i-1][j]+s[i][j-1]+a[i][j]-s[i-1][j-1];  //求前缀和
        }
    }
    while(q--)
    {
        int x1,y1,x2,y2;
        scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
        printf("%d\n",s[x2][y2]-s[x2][y1-1]-s[x1-1][y2]+s[x1-1][y1-1]);
    }
    return 0;
}

8差分 时间复杂度 o(m)

#include<iostream>
using namespace std;
const int N = 1e5 + 10;
int a[N], b[N];
int main()
{
    int n, m;
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++)
    {
        scanf("%d", &a[i]);
        b[i] = a[i] - a[i - 1];      //构建差分数组
    }
    int l, r, c;
    while (m--)
    {
        scanf("%d%d%d", &l, &r, &c);
        b[l] += c;     //将序列中[l, r]之间的每个数都加上c
        b[r + 1] -= c;
    }
    for (int i = 1; i <= n; i++)
    {
        a[i] = b[i] + a[i - 1];    //前缀和运算
        printf("%d ", a[i]);
    }
    return 0;
}

9二维矩阵差分:

#include<iostream>
#include<cstdio>
using namespace std;
const int N = 1e3 + 10;
int a[N][N], b[N][N];
void insert(int x1, int y1, int x2, int y2, int c)
{
    b[x1][y1] += c;
    b[x2 + 1][y1] -= c;
    b[x1][y2 + 1] -= c;
    b[x2 + 1][y2 + 1] += c;
}
int main()
{
    int n, m, q;
    cin >> n >> m >> q;
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= m; j++)
            cin >> a[i][j];
    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++)
        {
            printf("%d ", b[i][j]);
        }
        printf("\n");
    }
    return 0;
}

10给定一个长度为 n 的整数序列,请找出最长的不包含重复的数的连续区间,输出它的长度。 维护区间,移动的时候以右边那个为准,直到他的次数为一才行。

#include <iostream>
using namespace std;
const int N = 100010;
int a[N], s[N];
int main()
{
    int n, r = 0;
    cin >> n;
    for (int i = 0, j = 0; i < n; ++ i)
    {
        cin >> a[i];
        ++ s[a[i]];
        while (s[a[i]] > 1) -- s[a[j++]]; // 先减次数后右移
        r = max(r, i - j + 1) ;
    }
    cout << r;
    return 0;
}

11给定两个升序排序的有序数组 A 和 B,以及一个目标值 x。

数组下标从 0 开始。
请你求出满足 A[i]+B[j]=x 的数对 (i,j)。

#include <iostream>
using namespace std;
const int N = 100010;
int a[N], b[N];
int main()
{
    int n ,m ,x;
    scanf("%d%d%d", &n, &m, &x);
    for (int i = 0; i < n; i ++ ) scanf("%d", &a[i]);
    for (int j = 0; j < m; j ++ ) scanf("%d", &b[j]);
    for (int i = 0, j = m - 1; a[i] < x; i ++ )
    {
        while(a[i] + b[j] > x) j --;        // 重要的地方在这!
        if (a[i] + b[j] == x)
        {
            printf("%d %d", i, j);
            break;
        }
    }
    return 0;
}

12判断子序列

#include<iostream>
#include<cstdio>
using namespace std;
const int N=1e5+10;
int a[N],b[N];
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i = 0;i < n; i++) scanf("%d",&a[i]);
    for(int j = 0;j < m; j++) scanf("%d",&b[j]);
    int i = 0;
    for(int j = 0;j < m; j++)
    {
        if(i < n&&a[i] == b[j])  i++;
    }
    if(i == n) puts("Yes");
    else puts("No");
    return 0;
}

13二进制中的一的个数

使用lowbit操作,进行,每次lowbit操作截取一个数字最后一个1后面的所有位,每次减去lowbit得到的数字,直到数字减到0,就得到了最终1的个数,
lowbit原理
根据计算机负数表示的特点,如一个数字原码是10001000,他的负数表示形势是补码,就是反码+1,反码是01110111,加一则是01111000,二者按位与得到了1000,就是我们想要的lowbit操作

C++ 代码

#include<iostream>
using namespace std;
int lowbit(int x){
    return x&(-x);
}
int main(){
    int n;
    cin>>n;
    while(n--){
        int x;
        cin>>x;
        int res=0;
        while(x) x-=lowbit(x),res++;
        cout<<res<<' ';
    }
    return 0;
}

暴力法:
#include<iostream>
using namespace std;
int n;
int a,k;
int main(){
    scanf("%d",&n);
    for(int i=0;i<n;i++){
        scanf("%d",&a);
        k=0;
        while(a){
            k+=a&1;
            a=a>>1;
        }
        printf("%d ",k);
    }
    return 0;
}

14离散化:

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
typedef pair<int, int> PII;
const int N = 300010;
int a[N], s[N];
int n, m;
vector<int> alls;
vector<PII> add, query;
int find(int x)
{
    int l = 0, r = alls.size() - 1;
    while(l < r)
    {
        int mid = l + r >> 1;
        if(alls[mid] >= x) r = mid;
        else l = mid + 1;
    }
    return r + 1;
}
vector<int>:: iterator unique(vector<int> &a)
{
    int j = 0;
    for(int i = 0; i < a.size(); i ++)
        if(!i || a[i] != a[i - 1])
            a[j ++ ] = a[i];
    return a.begin() + j;
}
int main()
{
    cin >> n >> m;
    for(int i = 0; i < n; i ++ )
    {
        int x, c;
        cin >> x >> c;
        add.push_back({x, c});
        alls.push_back(x);
    }
    for(int i = 0; i < m; i ++ )
    {
        int l, r;
        cin >> l >> r;
        query.push_back({l, r});
        alls.push_back(l);
        alls.push_back(r);
    }
    sort(alls.begin(), alls.end());
    alls.erase(unique(alls), alls.end());
    for(auto item : add)
    {
        int x = find(item.first);
        a[x] += item.second;
    }
    for(int i = 1; i <= alls.size(); i ++ ) s[i] = s[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;
}

15区间合并:

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std ;
typedef pair<int,int> pii ;
vector<pii> nums,res ;
int main()
{
    int st=-2e9,ed=-2e9 ;      //ed代表区间结尾,st代表区间开头
    int n ;
    scanf("%d",&n) ; 
    while(n--)
    {
        int l,r ; 
        scanf("%d%d",&l,&r) ;
        nums.push_back({l,r}) ;
    }
    sort(nums.begin(),nums.end()) ;                 //按左端点排序
    for(auto num:nums)                   
    {
        if(ed<num.first)               //情况1:两个区间无法合并
        {
            if(ed!=-2e9) res.push_back({st,ed}) ;   //区间1放进res数组
            st=num.first,ed=num.second ;            //维护区间2
        }
        //情况2:两个区间可以合并,且区间1不包含区间2,区间2不包含区间1
        else if(ed<num.second)  
            ed=num.second ;                         //区间合并
    }  
    //(实际上也有情况3:区间1包含区间2,此时不需要任何操作,可以省略)
    //注:排过序之后,不可能有区间2包含区间1
    res.push_back({st,ed});
    //考虑循环结束时的st,ed变量,此时的st,ed变量不需要继续维护,只需要放进res数组即可。
    //因为这是最后的一个序列,所以不可能继续进行合并。
    printf("%d",res.size()) ;           //输出答案
    return 0 ;
}

16高精度加减乘除

1高精度加法:

#include <iostream>
#include <vector>
using namespace std;
const int n =1e6 + 5;
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=t/10;
    }
    if(t) c.push_back(1);
    return c;
}
int main()
{
    string a,b;
    vector<int> A,B;
    cin>>a>>b;
    for(int i=a.size()-1;i>=0;i--){
            A.push_back(a[i]-'0');
    }
    for(int i=b.size()-1;i>=0;i--){
            B.push_back(b[i]-'0');
    }
    auto c=add(A,B);
    for(int i=c.size()-1;i>=0;i--){
        printf("%d",c[i]);
    }
    return 0;
}

2高精度减法:

#include <iostream>
#include <vector>
using namespace std;

bool cmp(vector<int>& a, vector<int>& b)
{
    if(a.size()!=b.size()) return a.size()>b.size();
    for(int i=a.size()-1;i>=0;i--)
    {
        //最高位不相等
        if(a[i]!=b[i]) return a[i]>b[i];
    }
    return true;
}

vector<int> sub(vector<int> &a,vector<int> &b){   //这里a是大于b的
    vector<int> c;
    for(int i=0,t=0;i<a.size();i++){
        t=a[i]-t;
        if(i<b.size()) t=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();
    return c;
}


int main()
{
    string a,b;
    vector<int> A,B;
    cin>>a>>b;
    for(int i=a.size()-1;i>=0;i--){
            A.push_back(a[i]-'0');
    }
    for(int i=b.size()-1;i>=0;i--){
            B.push_back(b[i]-'0');
    }
    if(cmp(A,B)){
        auto c=sub(A,B);
        for(int i=c.size()-1;i>=0;i--)
            printf("%d",c[i]);
    }
    else
    {
        auto c=sub(B,A);
        printf("-");
        for(int i=c.size()-1;i>=0;i--)
            printf("%d",c[i]);
    }
    return 0;
}

3高精度乘法

#include <iostream>
#include <vector>
using namespace std;
vector <int> mul(vector <int> & A, int b) {
    vector <int> C;
    int t = 0;
    for (int i = 0; i < A.size(); i ++) {
        t += A[i] * b;    
        C.push_back(t % 10);
        t /= 10;        
    }
    while (t) {          
        C.push_back(t % 10);
        t /= 10;
    }
    while (C.size() > 1 && C.back() == 0) C.pop_back();
    return C;
}
int main() {
    string a;
    int b;
    cin >> a >> b;
    vector <int> A;
    for (int i = a.size() - 1; i >= 0; i --) A.push_back(a[i] - '0');
    auto  C = mul(A, b);
    for (int i = C.size() - 1; i >= 0; i --) {
        cout << C[i];
    }

    return 0;
}

4高精度除法

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
vector<int> div(vector<int> &A,int b,int &r) 
{
	vector<int> C;
	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;
}

int main()
{
	string a;
	int b;
	cin>>a>>b;
	vector<int> A;
	for(int i=a.size()-1;i>=0;i--) A.push_back(a[i]-'0');
	int r;
    auto C=div(A,b,r);
    for(int i=C.size()-1;i>=0;i--) cout<<C[i];
    cout<<endl<<r<<endl;

    return 0;  
}   
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值