C++基础算法总结

本文详细介绍了快速排序和归并排序的实现原理及代码实现,并提供了直接使用的排序函数示例。此外,还介绍了二分法的应用技巧、高精度运算的处理方法、前缀和与差分的应用场景,以及双指针算法的优化思路。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

快排

时间复杂度为O(nlogn),注意边界问题,最好将下边的算法过程背过。
快排的话一般上机考试很少用到,但是面试的时候面试官会让手写快排

#include<iostream>
using namespace std;

const int N = 100010;
int q[N];
int n;

void quick_sort(int q[],int l,int r){
    
    if(l >= r) return;
    
    int x = q[(l+r)/2], i = l-1, j = r+1;	
    //标记值x取中间,不要取第一个或者最后一个,如果数组里全是相同的数字,那么复杂度就变成了n²
    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);
    int i;
    for(i=0;i<n;i++)
        scanf("%d",&q[i]);
    quick_sort(q, 0, n-1);
    for(i=0;i<n;i++)
        printf("%d ",q[i]);
    return 0;
}

归并排序

时间复杂度为O(nlogn),是稳定排序

#include<iostream>
using namespace std;
const int N = 1e5 + 10;
int n;
int a[N],temp[N];

void merge_sort(int a[],int l,int r){
    int mid;
    if(l >= r)  return;
    mid = (l+r)/2;
    merge_sort(a,l,mid);
    merge_sort(a,mid+1,r);
    int i = l, j = mid+1;
    int k = 0;
    while(i<=mid && j<=r){
        if(a[i]<=a[j])
            temp[k++] = a[i++];
        else
            temp[k++] = a[j++];
    }
    while(i<=mid)   temp[k++] = a[i++];
    while(j<=r)     temp[k++] = a[j++];
    for(i=l,j=0;i<=r;i++,j++)
        a[i] = temp[j];
}

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

直接使用排序函数

//包含头文件
#include<algorithm>
//使用方法
//对a[10]进行排序
sort(a,a+10);

二分法

注意二分的什么时候需要+1,很容易陷入死循环

        int l = 0, r = n-1;
        int mid;
        //r = mid 不需要加1
        while(l<r){
            mid = (l+r)/2;
            if(k<=a[mid])    r = mid;
            else l = mid + 1;
        }

		//l = mid需要加1
        l=0;
        r=n-1; 
        while(l<r){
            mid = (l+r+1)/2;
            if(k>=a[mid]) l = mid;
            else    r = mid - 1;
        }

高精度运算

对于高精度的加减乘除法,由于位数太多数字变量无法储存,所以需要将其储存在数组中。由于运算需要有进位和借位,所以对于加、减、乘需要逆序存储,而对于除法直接顺序存储就可以。

前缀和(常用

一维前缀和

计算数组a[l,r]区间内的和
注意下标需要从1开始,便于处理边界。

#include<iostream>
using namespace std;
const int N = 1e5 +10;
int a[N];
int s[N];

int n,m;
int main(){
    cin>>n>>m;
    int i;
    int l,r;
    for(i=1;i<=n;i++)
        scanf("%d",&a[i]);
    for(i=1;i<=n;i++)		//计算所有前缀和
        s[i] = s[i-1] + a[i];
    for(i=0;i<m;i++){
        scanf("%d%d",&l,&r);
            printf("%d\n",s[r]-s[l-1]);		//输出[l,r]区间的前缀和
    }
    return 0;
}
二维前缀和(子矩阵的和)

注意记住公式,而且下标要从0开始

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

差分

差分是前缀和的逆运算。

一维差分

对于区间[l,r]上的每个数加上c,时间复杂度为O(1)。

#include<iostream>
using namespace std;
const int N = 1e5 + 10;
int a[N];
int b[N];
int n,m;

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

int main(){
    cin>>n>>m;
    int i;
    int l,r,c;
    for(i=1;i<=n;i++)
        scanf("%d",&a[i]);
    for(i=1;i<=n;i++)
        insert(i,i,a[i]);       //假设为0,执行n次插入进行赋值
    while(m--){
        cin>>l>>r>>c;
        insert(l,r,c);
    }
    for(i=1;i<=n;i++)       //计算自身的前缀和并储存在自身
        b[i] += b[i-1];
    for(i=1;i<=n;i++)
        printf("%d ",b[i]);
    return 0;
}
二维差分

对(x1,y1)和(x2,y2)区间里的数加上c,本来需要O(n)进行枚举,现在只需要改变4个数。

#include<iostream>
using namespace std;
const int N = 1010;
int a[N][N];
int b[N][N];
int n,m,q;

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(){
    cin>>n>>m>>q;
    int i,j;
    int x1,y1,x2,y2,c;
    for(i=1;i<=n;i++)
        for(j=1;j<=m;j++)
            scanf("%d",&a[i][j]);
    for(i=1;i<=n;i++)               //对二维数组进行初始化
        for(j=1;j<=m;j++)
            insert(i,j,i,j,a[i][j]);
    while(q--){
        cin>>x1>>y1>>x2>>y2>>c;
        insert(x1,y1,x2,y2,c);
    }
    for(i=1;i<=n;i++){              //计算自身的前缀和并储存
        for(j=1;j<=m;j++){
            b[i][j] += b[i-1][j] + b[i][j-1] - b[i-1][j-1];
        }
    }
    for(i=1;i<=n;i++){
        for(j=1;j<=m;j++){
            printf("%d ",b[i][j]);
        }
        printf("\n");
    }
    return 0;
}

双指针算法

核心思想是将O(n²)的暴力枚举变为O(n)。比较常见,主要是一种解题思考的思路,快排和归并排序等都有用到双指针算法。
解题思路:先用暴力方法写出来,然后看 i 和 j 之间有没有单调关系,如果有,就可以从暴力变为双指针。

for(int i=0,int j=0;i<n;i++){
	while(j<i && check(i,j))
		j++;
		
	//算法对应的题目要求
}

位运算

  1. n的二进制表示中第k位是几
    ① 先把第k位移到最后,n>>k
    ② 看个位是几,n&1
  2. lowbi(x) 返回x的最右边的最后一位1,
    表达式: x & -x

离散化

掌握离散化的思想,如果数据值达到1e9,那么对应的数组不能开到1e9这么大,但是如果输入是稀疏离散的,可以按照输入顺序进行存储数据值,那么数组只用开到1e5的大小。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值