C++算法基础

目录

 1.快速排序

2.归并排序

3.整数二分

4.浮点数二分

5.高精度

高精度正整数加法:

高精度正整数减法:

高精度正整数乘法:

高精度正整数除法:

6.前缀和

7.差分(前缀和的逆运算):

8.双指针算法:

9.位运算:

10.离散化:

11.区间和并


 1.快速排序

①确定分界点 q[l] q[(l+r)/2] q[r] ★

②调整区间 下标i j 分别从两边遍历

③递归处理左右两段

#include <iostream>
using namespace std;
const int N = 1e6+10;

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

    quick_sort(q, l, j);
    quick_sort(q, j+1, r);
}
int main()
{
    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;
}

2.归并排序

①确定分界点 mid = (l+r)/2

②递归排序left right 左右两个数组

③归并排序 合二为一 ★

#include <iostream>
using namespace std;
const int N = 1e6+10;

int n;
int q[N], tmp[N];
void merge_sort(int q[], int l, int r)
{
    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])    tmp[k++] = q[i++];
        else    tmp[k++] = q[j++];
    while(i <= mid) tmp[k++] = q[i++];
    while(j <= r)   tmp[k++] = q[j++];

    for(i = l, k = 0; i <= r; i++, k++) q[i] = tmp[k];
}
int main()
{
    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;
}

3.整数二分

① mid = l + r + 1 / 2  

x在 l 和 mid 之间 则 r = mid-1 [l, mid - 1];

x在 mid 和 r 之间 则 l = mid [mid, r];

// 区分[l, r]被划分成[l, mid]和[mid + 1, r]时使用
#include <iostream>

using namespace std;
int bsearch_1(int q[], int size, int target)
{
    int l = 0, r = size - 1;
    while(l < r)
    {
        int mid = l + r >> 1;
        if(q[mid] >= target) r = mid;
        else l = mid + 1;
    }
    if(q[l] != target) return -1;
    return l;   
}

// 区分[l, r]被划分成[l, mid-1]和[mid, r]时使用
int bsearch_2(int q[], int size, int target)
{
    int l = 0, r = size - 1;
    while(l < r)
    {
        int mid = l + r + 1 >> 1;
        if(q[mid] <= target) l = mid;
        else r = mid - 1;
    }
    if(q[l] != target) return -1;
    return l;   
}

4.浮点数二分

double bsearch_f(double q[], int size, double target)
{
    const double eps = 1e-6;   // eps 表示精度,取决于题目对精度的要求
    int l = 0, r = size - 1;
    while (r - l > eps)  // 两种写法:此时是用精度控制循环次数,直接控制循环100次也是OK的!
    {
        int mid = (l + r) / 2;
        if (q[mid] >= target) r = mid;
        else l = mid + 1;
    }
    if(q[l] != target) return -1;
    return l;
}

5.高精度

高精度整数 比较大的数 A B  10^6位 

①大整数存储

②运算 

A + B 位数 10^6位

A - B 位数 10^6位

A * a  len(A) <=10^6  a <= 10^9

A / a

高精度正整数加法:

#include <iostream>
#include <vector> 

using namespace std;

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);
    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;
}

高精度正整数减法:

#include <iostream>
#include <vector> 

using namespace std;

// 判断A是不是大于B
bool cmp(vector<int> &A, vector<int> &B)
{
    if(A.size() != B.size())    return A.size() > B.size();
    for (int i = 0; i < A.size(); i++)
    {
        if(A[i] != B[i]) return A[i] > B[i];       
    }
    return true;
}

// C = A - B
vector<int> sub(vector<int> &A, vector<int> &B)
{
    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 答案为007此情况
    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;
}

高精度正整数乘法:

#include <iostream>
#include <vector> 

using namespace std;

// C = A * b
vector<int> mul(vector<int> &A, int b)
{
    vector<int> C;
    int t = 0;
    for (int i = 0, t = 0; i < A.size() || t; i++)
    {
        if(i < A.size())    t += A[i] * b;
        C.push_back(t % 10);
        t /= 10;
    }
    return C;
}

int main()
{
    string a;
    vector<int> A;
    int b;
    cin >> a >> b;
    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--)  printf("%d", C[i]);    
    
    return 0;
}

高精度正整数除法:

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

// C = A / b 商 C 余数r
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;
    vector<int> A;
    int b;
    cin >> a >> b;
    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--)  printf("%d", C[i]);    
    cout << endl << r;
    return 0;
}

6.前缀和

数组an a1 a2 ... an

前缀和 Si = a1 + a2 + ... + ai

①如何求

②作用

// 一维数组前缀和的计算
#include <iostream>

using namespace std;

const int N = 100010;
int a[N], s[N];
int n, m;

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 ++)    s[i] = s[i - 1] + a[i]; // 前缀和的初始化
    while(m --)
    {
        int l, r;
        scanf("%d%d", &l, &r);
        printf("%d\n", s[r] - s[l - 1]); // 区间和的计算
    }
    return 0;
}
// 二位数组矩阵前缀和计算
#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\n", s[x2][y2] - s[x1-1][y2] - s[x2][y1-1] + s[x1-1][y1-1]);
    }
    return 0;
}

7.差分(前缀和的逆运算):

数组 a1 a2 ... an        前缀

构造 b1 b2 ... bn        差分

使得 ai = b1 + b2 + ... + bi

即 b1 = a1 b2 = a2 - a1 b3 = a3 - a2 ... bn = an- an-1

an 是 bn 的前缀和, bn 是 an 的差分

// 一维数组差分 
// 输入一个数组和执行数 执行 给序列l~r的每个数加上c
#include <iostream>

using namespace std;

const int N = 100010;

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]);

    while(m--)
    {
        int l, r, c;
        scanf("%d%d%d", &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 ++)    printf("%d ", b[i]);
    return 0;
}

        

// 二维数组差分 
// 输入一个二维数组和执行数 执行 给序列x1 y1 ~ x2 y2的每个数加上c
#include <iostream>
using namespace std;

const int N = 1010;

int n, m, t;
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()
{
    scanf("%d%d%d", &n, &m, &t);
    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 ++)
            insert(i, j, i, j, a[i][j]);

    while(t--)
    {
        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] << " ";
        cout << endl;
    }
    return 0;
}

8.双指针算法:

例题:输入一个字符串,将里面的每一个单词 各占一行打印出来

#include <iostream>
#include <string.h>
using namespace std;

const int N = 1000;
int main()
{
    char str[N];
    cin.getline(str,N);
    int n = strlen(str);

    for(int i = 0; i < n; i ++)
    {
        int j = i;
        while(j < n && str[j] != ' ')   j++;
        for (int k = i; k < j; k ++)    cout << str[k];
        cout << endl;
        i = j;
    }
    return 0;
}

例题:给定一个长度为n的整数序列,请找出最长的不包含重复数字的连续子序列,输出它的长度。

#include <iostream>
using namespace std;

const int N = 100010;
int n, 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(s[a[i]] > 1)
        {
            s[a[j]]--;
            j++;
        }
        res = max(res, i-j+1);
    }
    cout << res << endl;
    return 0;
}

9.位运算:

n的二进制表示 第k位 是 几

①先把第k位移到最后一位: n >> k

②看个位是几: & 1

 n >> k & 1

--------------

lowbit(x) :返回x的最后一位1 是多少 例如 1010 ---> 10,  101000 --->1000

实现 : x & -x = x & (~x + 1)

例: 输入一个数组返回数组中每个元素二进制形式中1的个数

#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;
}

10.离散化:

vector <int> alls; // 存储所有待离散化的值

sort(alls.begin(), alls.end()); // 将所有值进行排序

alls.erase(unique(alls.begin(), alls.end()), alls.end()); // 去除重复元素

// 二分求出x对应的离散化的值
int find(int x) // 找到第一个大于等于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; // 映射到1,2,...,n
}

例题: 输入一个数组长度和操作数,操作:x c 将数组a[x] = c,输入l,r ,输出a[l]+ ...+a[r]的和

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

typedef pair<int ,int> PII;

const int N = 300010;
int n, m;
int a[N], s[N];

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;
}

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.begin(), alls.end()), 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;
}

11.区间和并

例题:输入n 接下来的n行,每行输入两个整数l和r,比如2 4 则表示 2 3 4 这个区间,输出合并后的区间的个数。

①按区间左端点排序;②扫描整个区间,扫描过程中 进行分类合并

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

typedef pair<int, int> PII;
const int N = 100010;

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, ed = seg.second;
        }
        else    ed = max(ed, seg.second);
    
    if(st != -2e9)  res.push_back({st, ed});
    segs = res;
}

int n;
vector<PII> segs;

int main()
{
    cin >> n;

    for(int i = 0; i < n; i ++)
    {
        int l, r;
        cin>> l >> r;
        segs.push_back({l, r});
    }

    merge(segs);

    cout << segs.size() << endl;

    return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值