【基础算法】9大基础算法(排序,查找,前缀和与差分,离散化,区间合并...)

基础算法

1.快速排序

sort.png

分治

1.确定分界点x,左边,中间,右边

2.i ,j q[i]<x q[j]>x

3.分治递归

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

void quick_sort(int q[],int l,int r){
	if (l>=r) return;
	int x=q[(l+r)>>1],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;
}
  • Acwing 786 第k个数

2.归并排序

  • AcWing 787. 归并排序

1.确定分界点,mid=l+r>>1

2.左边右边分治

3.双指针,两个数组按顺序排序

 #include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N =1000010;

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,j=0;i<=r;i++,j++) q[i]=tmp[j] ;
	
}

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;
} 
  • AcWing 788. 逆序对的数量

3.二分查找

  • AcWing 789. 数的范围
/*
方法1:手写二分,注意分界点的情况,避免陷入死循环。 
*/

#include<iostream>
using namespace std;
const int maxn=100010;
int n,q,k,a[maxn];
int main(){
	scanf("%d%d",&n,&q);
	for (int i=0;i<n;i++) scanf("%d",&a[i]);
	while(q--){
		scanf("%d",&k);
		int l=0,r=n-1;

		while(l<r){
			int mid=l+r>>1;
			if (a[mid]<k) l=mid+1;
			else r=mid;
		}
		if (a[l]!=k){
			printf("-1 -1\n");
			continue;
		}
		int l1=l,r1=n;
		while(l1+1<r1){
			int mid=l1+r1>>1;
			if(a[mid]<=k) l1=mid;
			else r1=mid;
		}
		printf("%d %d\n",l,l1);
	}
	return 0;
}
/*
方法2:直接调用函数
binary_search(a,a+n,k)
lower_bound(a,a+n,k)-a
upper_bound(a,a+n,k)-a-1
*/ 

#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=100010;
int n,q,k,a[maxn];
int main(){
	scanf("%d%d",&n,&q);
	for (int i=0;i<n;i++) scanf("%d",&a[i]);
	
	while(q--){
		scanf("%d",&k);
		if(binary_search(a,a+n,k)){
			cout<<lower_bound(a,a+n,k)-a<<" "<<upper_bound(a,a+n,k)-a-1<<endl;
		}
		else{
			cout<<"-1 -1"<<endl;
		}
	}
	
	
	return 0;
}

4.高精度

  • AcWing 791. 高精度加法

5.前缀和与差分

    1. AcWing. 前缀和

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

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

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

#include<iostream>
using namespace std;
const int maxn=100010;
int n,m,l,r,a[maxn],s[maxn];
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin>>n>>m;
	for (int i=1;i<=n;i++) {
		cin>>a[i];
		s[i]=s[i-1]+a[i];
	}
	while(m--){
		cin>>l>>r;
		cout<<s[r]-s[l-1]<<endl;
	}
	return 0;
}
  • AcWing 796. 子矩阵的和
#include<iostream>
using namespace std;

const int maxn = 1001; // 定义最大数组大小,确保足够存储数据,最大为 1000x1000 的矩阵

int n, m, q; // n: 行数, m: 列数, q: 查询次数
int x1, y1, x2, y2; // 查询矩形区域的两个对角坐标
int a[maxn][maxn], s[maxn][maxn]; // a: 原始矩阵, s: 前缀和矩阵

int main(){
	ios::sync_with_stdio(false); // 禁用同步加速输入输出
	cin.tie(0); // 解除 cin 和 cout 的绑定,进一步提高输入输出速度

	cin >> n >> m >> q; // 输入矩阵的行数、列数和查询次数

	// 读取输入的矩阵,并计算二维前缀和
	for(int i = 1; i <= n; i++){
		for(int j = 1; j <= m; j++){
			cin >> a[i][j]; // 读入原始矩阵元素
			
			// 计算二维前缀和公式:
			// s[i][j] 表示从 (1,1) 到 (i,j) 矩形区域的元素和
			s[i][j] = s[i-1][j] + s[i][j-1] - s[i-1][j-1] + a[i][j];
		}
	}

	// 处理 q 次查询
	while(q--){
		cin >> x1 >> y1 >> x2 >> y2; // 输入矩形区域的左上角 (x1, y1) 和右下角 (x2, y2)

		// 使用二维前缀和公式计算 (x1, y1) 到 (x2, y2) 的矩形区域的元素和:
		// s[x2][y2] 表示从 (1,1) 到 (x2, y2) 的区域和
		// 减去多余的区域:s[x2][y1-1] 和 s[x1-1][y2]
		// 加回因为重叠部分被减了两次的 s[x1-1][y1-1]
		cout << s[x2][y2] - s[x2][y1-1] - s[x1-1][y2] + s[x1-1][y1-1] << endl;
	}
	
	return 0;
}

  • AcWing 797. 差分

差分数组:

首先给定一个原数组a:a[1], a[2], a[3], a[n];

然后我们构造一个数组b : b[1] ,b[2] , b[3], b[i];

使得 a[i] = b[1] + b[2 ]+ b[3] +, + b[i]

也就是说,a数组是b数组的前缀和数组,反过来我们把b数组叫做a数组的差分数组。换句话说,每一个a[i]都是b数组中从头开始的一段区间和。

#include<iostream>
using namespace std;
const int maxn=100010;
int l,r,c;
int n,m;
int a[maxn],b[maxn],s[maxn];
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		b[i]=a[i]-a[i-1];
	}
	while(m--){
		cin>>l>>r>>c;
		b[l]+=c;
		b[r+1]-=c;
	}
	for(int i=1;i<=n;i++){
		s[i]=s[i-1]+b[i];
		cout<<s[i]<<" ";
	}
	
	return 0;
}
  • AcWing 798. 差分矩阵
#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;
}


6.双指针算法

  • AcWing 799. 最长连续不重复子序列

维护一个双指针j,i。假设j到i-1是最长连续不重复子序列,那么j到i不是的话一定是a[i]重复了,将j的位置进行调整,向右移动找到重复的a[i]

#include<iostream>
using namespace std;
const int maxn=100001;
int n,r;
int a[maxn],s[maxn];
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin>>n;
	int j=1;
	for(int i=1;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;
}
  • AcWing 800. 数组元素的目标和
#include<iostream>
#include<algorithm> 
using namespace std;
const int maxn=100001;
int n,m,x;
int a[maxn],b[maxn],flag[maxn];
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin>>n>>m>>x;
	for(int i=0;i<n;i++) cin>>a[i];
	for(int i=0;i<m;i++) cin>>b[i]; 
	for(int i=0;i<=n;i++){
		if (binary_search(b,b+m,x-a[i])){
			cout<<i<<" "<<upper_bound(b,b+m,x-a[i])-b-1<<endl;
			break;
		}
	}
	return 0;
}



#include<iostream>
#include<algorithm> 
using namespace std;
const int maxn=100001;
int n,m,x;
int a[maxn],b[maxn],flag[maxn];

int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin>>n>>m>>x;
	for(int i=0;i<n;i++) cin>>a[i];
	for(int i=0;i<m;i++) cin>>b[i]; 
	for(int i=0;i<=n;i++){
		int y=x-a[i];
		int l=0,r=m-1;
		while(l<r){
			int mid=l+r>>1;
			if(b[mid]<y) l=mid+1;
			else r=mid;
		}
		if(b[l]==y){
			cout<<i<<" "<<l<<endl;
			break;
		}
	}
	return 0;
	
	
	return 0;
}
  • AcWing 2816. 判断子序列

遍历数组看看有没有子序列。如果j<m则说明没有包含。

#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=100001;
int a[maxn],b[maxn];
int n,m;
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin>>n>>m;
	for(int i=0;i<n;i++) cin>>a[i];
	for(int i=0;i<m;i++) cin>>b[i];
	int j=0;
	for (int i=0;i<m;i++){
		if(b[i]==a[j]) j++;
	} 
//	cout<<j<<endl;
	if(j>=n){
		cout<<"Yes";
	}else{
		cout<<"No";
	}
	
	return 0;
}

7.位运算

lowbit原理
根据计算机负数表示的特点,如一个数字原码是10001000,他的负数表示形势是补码,就是反码+1,反码是01110111,加一则是01111000,二者按位与得到了1000,就是我们想要的lowbit操作。

  • AcWing 801. 二进制中1的个数

(lowbit) O(nlogn)
使用lowbit操作,进行,每次lowbit操作截取一个数字最后一个1后面的所有位,每次减去lowbit得到的数字,直到数字减到0,就得到了最终1的个数,

#include<iostream>
using namespace std;
const int maxn=10010;
int n;
int lowbit(int x){
	return x&(-x);
}

int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin>>n;
	while(n--){
		int x,cnt=0;
		cin>>x;
		while(x){
			x-=lowbit(x);
			cnt+=1;
		}
		cout<<cnt<<" ";
	}
	
	
	
	return 0;
}

8.离散化

  • AcWing 802. 区间和

由于题目区间范围为−109≤x≤109,比较大,但是数值只有1e5,所以可以将区间离散化。

可以将所有的下标都存到vector里面,然后排序去重,就相相当于按顺序离散化了

然后通过二分查找找到真实的数在vertor里的位置,就当作离散化后位置。

然后前缀和。

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
const int maxn=300010;
int n,m;
int a[maxn];  //离散化之后存储插入的值 
int s[maxn];  //存储数组a的前缀和
vector<int> alls;    //存储所有(插入和查询)的坐标
typedef pair<int,int> PII;
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) l=mid+1;
		else r=mid; 
	}
	return r+1;
}

int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	cin>>n>>m ;
	for(int i=1;i<=n;i++){
		int x,c;
		cin>>x>>c;
		add.push_back({x,c});
		alls.push_back(x) ;
	}
	
	for(int i=1;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());
	//执行前n次插入
	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];
	//处理后m次查询操作
	for(auto item:query){
		int l=find(item.first);
		int r=find(item.second);
		cout<<s[r]-s[l-1]<<endl;
	} 
	
	return 0;
} 

9.区间合并

  • AcWing 803. 区间合并
    • 按照区间第一个点来排序
    • 然后来维护区间,判断当前区间与后面区间的情况,一共有三种
    • 如果两个区间无法合并,则区间数量加1;
    • 如果两个区间可以合并,并且互不包含,则区间合并
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=1e5+10;
typedef pair<int,int> PII;
vector<PII> nums,res;

int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
	int st=-2e9,ed=-2e9;   //区间结尾和开头;
	int n;
	cin>>n;
	while(n--){
		int l,r;
		cin>>l>>r;
		nums.push_back({l,r});
	} 
	
	sort(nums.begin(),nums.end());
	for (auto num:nums){
		//情况1,两个区间无法合并 
		if(ed<num.first){
			if(ed!=-2e9) res.push_back({st,ed});
			st=num.first,ed=num.second;
		}
		//情况2,两个区间可以合并,且区间11不包含区间2,区间2不包含区间1;
		else if (ed<num.second) ed=num.second; //区间合并; 
	}
	res.push_back({st,ed});
	cout<<res.size(); 
	
	return 0;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值