算法从入门到入土cpp版

1. 排序

1. 快速排序

# include<iostream>
using namespace std;

const int N = 100010;

int q[N];

void quick_sort(int q[], int l, int r)
{
    if(l>=r) return;
    int i=l-1,j=r+1,temp=q[l];
    while(i<j)
    {
        do i++;while(q[i]<temp);
        do j--;while(q[j]>temp);
        if(i<j)
            swap(q[i], q[j]);
    }
    quick_sort(q, l, j);
    quick_sort(q, j+1, r);
}

int main()
{
    int n;
    scanf("%d", &n);
    for(int i=0;i<n;i++)
        scanf("%d", &q[i]);
    
    quick_sort(q, 0, n-1);
    
    for(int i=0;i<n;i++)
        printf("%d ", q[i]);
    return 0;
}

从无序列数组中找有序的第k个数,其实改一下输出就可以了

# include<iostream>
using namespace std;

const int N = 100010;

int q[N];

void quick_sort(int q[], int l, int r)
{
    if(l>=r) return;
    int i=l-1,j=r+1,temp=q[l];
    while(i<j)
    {
        do i++;while(q[i]<temp);
        do j--;while(q[j]>temp);
        if(i<j)
            swap(q[i], q[j]);
    }
    quick_sort(q, l, j);
    quick_sort(q, j+1, r);
}

int main()
{
    int n;
    int x;
    scanf("%d", &n);
    scanf("%d", &x);
    for(int i=0;i<n;i++)
        scanf("%d", &q[i]);
    
    quick_sort(q, 0, n-1);
    
    printf("%d", q[x-1]);
    return 0;
}

2. 归并排序

核心:分治-》双指针问题

  • 找分界点
  • 递归排序左右
  • 归并-》合二为一
#include<iostream>

using namespace std;
const int N = 100010;

void merge_sort(int q[], int l, int r)
{
    int temp[N]; //辅助数组
    if(l>=r) return;
    int mid=(l+r)>>1; //取中间值作为分界点
    //递归排序左右
    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])  temp[k++]=q[i++];
        else temp[k++]=q[j++];
    }
    //当一只先用完,另一只直接挂在temp数组后
    while(i<=mid) temp[k++]=q[i++];
    while(j<=r) temp[k++]=q[j++];
    
    //将结果从辅助数组转移到原数组
    for(int i=l,j=0; i<=r; i++,j++)
        q[i]=temp[j];
}

int q[N];
int main()
{
    int n;
    scanf("%d", &n);
    for(int i=0;i<n;i++)    
        scanf("%d", &q[i]);
    
    merge_sort(q, 0, n-1);
    
    for(int i=0;i<n;i++)
        printf("%d ", q[i]);
    
    return 0;
}

2. 高精度加减乘除

cpp没有内置很大的数,很大的数在cpp里是以数组形式存储,所以大数运算需要定义
加法:

#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]; //t=t+A[i]+B[i]
        
        C.push_back(t%10); //对t取整
        
        t /= 10;//求向高位的进位
    }
    
    if(t!=0) C.push_back(1); //超过位数有进位高位需要补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'); //注意a是string类型,转成int要减去‘0’
    for(int j=b.size()-1; j>=0; j--) B.push_back(b[j]-'0');
    
    //auto 编译器会自动判断C的类型
    auto C = add(A, B);
    
    for(int i=C.size()-1;i>=0;i--) printf("%d", C[i]);
    
    return 0;
}

减法

#include<iostream>
#include<vector>

using namespace std;

const int N = 1e6+10;

//比较两数大小
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; //相等返回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]; //t = A-B-t
        C.push_back((t+10)%10);  //如果t小于0要借位+10
        
        if(t<0) t=1; //看是否向高位借位
        else t=0;
    }
    //去除高位的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'); //注意a是string类型,转成int要减去‘0’
    for(int j=b.size()-1; j>=0; j--) B.push_back(b[j]-'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. 前缀和

一维数组前缀和

输入一个长度为 n的整数序列。

接下来再输入 m 个询问,每个询问输入一对 l,r。

对于每个询问,输出原序列中从第 l个数到第 r个数的和。

#include<iostream>
using namespace std;

const int N = 100010;
int n, m;

int a[N], s[N];

int main()
{
    scanf("%d", &n);
    scanf("%d", &m); //询问次数
        
    for(int i=1; i<=n; i++) scanf("%d", &a[i]); 
    //构造出前缀和序列
    for(int i=1; i<=n; i++) s[i]=s[i-1]+a[i];
    
    while(m--)//m次询问
    {
        int l,r;
        scanf("%d", &l);
        scanf("%d", &r);
        printf("%d\n", s[r]-s[l-1]); //求出特定子区间的和
    }
    return 0;
}

一维求出来了,可以扩展到二维,一定要记住是行和列不是坐标!!!!!

子矩阵的和

输入一个 n 行 m列的整数矩阵,再输入 q 个询问,每个询问包含四个整数 x1,y1,x2,y2,表示一个子矩阵的左上角坐标和右下角坐标。
对于每个询问输出子矩阵中所有数的和。
输入格式
第一行包含三个整数 n,m,q
接下来 n 行,每行包含 m个整数,表示整数矩阵。
接下来 q行,每行包含四个整数 x1,y1,x2,y2,表示一组询问。

#include<iostream>
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] - S[i-1][j-1] + a[i][j]; //构造前缀和数组
        
    }
        
    while(q--)
    {
        int x1, y1, x2, y2;
        scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
        printf("%d", S[x2][y2] - S[x1-1][y2] - S[x2][y1-1] + S[x1-1][y1-1]);
        return 0;
    }

}

输入
3 4 3
1 7 2 4
3 6 2 8
2 1 2 3
1 1 2 2
2 1 3 4
1 3 3 4

输出
17
运行时间:2ms

4. 差分

差分其实就是前缀和的逆运算,

一维差分

一维差分主要用途:实现O(1)复杂度的在数组的某个区间上加上或者减去某个数。
eg:
输入一个长度为 n 的整数序列。
接下来输入 m个操作,每个操作包含三个整数 l,r,c,表示将序列中 [l,r]之间的每个数加上 c。
输出进行完所有操作后的序列。
输入格式:

  • 第一行包含两个整数 n和 m
  • 第二行包含 n个整数,表示整数序列。
  • 接下来 m 行,每行包含三个整数 l,r,c,表示一个操作。
    输出格式
    共一行,包含 n个整数,表示最终序列。
#include<iostream>
using namespace std;
const int N = 100010;

int a[N], b[N];//a数组用来接收原始数组,b数组用来构造差分数组
int main()
{
    int n, m; //m表示询问的次数
    scanf("%d%d", &n, &m);
    for(int i=1; i<=n; i++)
    {
        scanf("%d", &a[i]);
    }
    
    //构造差分数组
    for(int i=1; i<=n; i++)
        b[i]=a[i]-a[i-1];
    
    int l, r, c;
    
    while(m--)
    {
        scanf("%d%d%d", &l, &r, &c);
        b[l] += c; //将区间内加c
        b[r+1] -= c; //超出区间-c,相当于使原数组不变
    }
    
    //求前缀和
    for(int i=1; i<=n; i++)
    {
        b[i] += b[i-1];
        printf("%d ", b[i]);
    }
    return 0;
}

输入
6 3
1 2 2 1 2 1
1 3 1
3 5 1
1 6 1

输出
3 4 5 3 4 2
运行时间:1ms

#include<iostream>
using namespace std;

const int N = 10010;

int n, m;
int a[N], b[N];

void insert(int l, int r, int c)
{
    b[l] += c;
    b[r+1] -=c;
}

int main()
{
    scanf("%d%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]); //向b中(i, i)位置插入数值为a[i]
        
    int l, r, c;
    
    while(m--)
    {
        scanf("%d%d%d", &l, &r, &c);
        insert(l, r, c);
    }
    
    for(int i=1;i<=n;i++)
        b[i] += b[i-1]; //求b的前缀和就是数组a
    for(int i=1;i<=n; i++)
        printf("%d ", b[i]); //输出b的前缀和数组就是要求处理后的a
    return 0;
}

二维差分

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值