我最爱的yyyyy总基础算法 studying note

经典排序

快速排序

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

void quick_sort(int q[],int l,int r){
    int i=l-1,j=r+1;
    int x=q[l];
    if(l>=r)return;//表示左右区间中只有一个数或没有数
    while(i<j){
        do i++;while(q[i]<x);
        do j--;while(q[i]>x);
        if(i<j)swap(q[i],q[j]);
        
    }
    quick_sort(q,l,j);//quick_sort(q,l,i-1);
    quick_sort(q,j+1,r);//quick_sort(q,i,r);  
  //!!!注意:如果写i 则要考虑边界问题  不要将前面的x定义为q【l】
	// 同样取i时需要上取整 i=(l+r+1)>>1;
}

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

快速选择算法

思路: 先用快排将整个序列排好
再比较k与j
再递归到k所在的区间内进行下一步快排

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

void quick_sort(int l,int r,int k){
    int i=l-1,j=r+1;
    int x=q[l];
    if(l>=r)return;//表示左右区间中只有一个数或没有数
    while(i<j){
        do i++;while(q[i]<x);
        do j--;while(q[i]>x);
        if(i<j)swap(q[i],q[j]);
        
    }
    int sl=j-l+1;
    
    if(k<=sl)return quick_sort(l,j,k);
    
	return quick_sort(j+1,r,k-sl);
}

int main()
{
    int n,k;
    int a[N];
    scanf("%d %d",&n,&k);
    for(int i=0;i<n;i++)scanf("%d",&a[i]);
    cout<<quick_sort(0,n-1,k)<<endl;
    
    return 0;
    
}

归并排序

在这里插入图片描述

#include<iostream>
using namespace std;
const int N=1e6+10;
int q[N],tmp[N];
void merge_sort(int q[],int l,int r){
	int i=l-1,j=r+1;
	int mid=l+r>>1;
	if(l>=r) return ;
	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++];
	
	//重新放回q数组中 
	for(int i=1,j=0;i<=r;i++,j++)q[i]=tmp[j];
	}
	
}

逆序数的算法

#include<iostream>
using namespace std;
const int N=1e6+10;
int q[N],tmp[N];
typedef long long ll;
ll merge_sort(int l,int r){
	int i=l-1,j=r+1;
	int mid=l+r>>1;
	if(l>=r) return ;
	ll res=merge_sort(l,mid)+merge_sort(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++];
		res+=mid-i+1;
}
	while(i<=mid)tmp[k++]=q[i++];
	while(j<=r)tmp[k++]=q[j++];
	
	//重新放回q数组中 
	for(int i=1,j=0;i<=r;i++,j++)q[i]=tmp[j];
	}
	return res;
}

二分法

二分查找

数的范围 确定

在这里插入图片描述

法一:
//确定边界
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";
//二分一定有解  题目查找不一定有解 
法二:
int l=0,r=n-1;
while(l<r){
	int mid=l+r>>1;
	if(q[mid]<=x)l=mid;
	else r=mid-1;
} 

经典例题:数的三次方根

#include"bits/stdc++.h"
using namespace std;
double n;
int main()
{
	cin>>n;
	int l=-10000,r=10000;
	while(r-l>1e-8){
		double mid= r+l>>1;
		if(mid*mid*mid>=n)r=mid;
		else l=mid;
	}
	cout<<l<<endl;
	return 0; 
 } 

高精度加法

#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];
		c.push_back(t%10);
		t/=10;
		
	}
	if(t) c.push_back(1);
 } 
 
 
 int main()
 {
 	string s,ss;
 	vector<int>a,b;
 	
 	cin>>s>>ss;
 	for(int i=s.size()-1;i>=0;i--)a.push_back(s[i]-'0');
 	for(int i=ss.size()-1;i>=0;i--)b.push_back(ss[i]-'0');
 	auto c=add(a,b);
 
 	for(int i=s.size()-1;i>=0;i--)cout<<c[i]<<endl; 
 }  

前缀和

将前n个数值做处理 要求某个区间和能方便快速

#include<iostream>

using namespace std;

const int N=1e5+10;

ll n,m;

ll a[N],b[N];
int main()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++)cin>>a[i];
	
	b[0]=0;
	for(int j=1;j<=n;j++)b[i]=b[i-1]+a[i];
	
	while(m--){
		int l,r;
		cin>>l>>r;
		cout<<b[r]-b[l-1]<<endl;	 
	}
}

子矩阵的和

在这里插入图片描述

#include<bits/stdc++.h>
using namespace std;

const int N=1010; 
int a[N][N];
int s[N][N];
int main()
{
	int n,m;
	cin>>n>>m>>q;
	for(int i=0;i<n;i++){
		for(int j=0;j<m;j++)
			cin>>a[i][j];
			//同时将二维前缀和初始化 
			s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+a[i][j];
	}
	
	
	while(q--){
		int x1,x2,y1,y2;
		cin>>x1>>y1>>x2>>y2;
		cout<<s[x2][y2]-s[x2][y1-1]-s[x1-1][y2]+s[x1-1][y1-1]<<endl;
	}
}

差分

差分和前缀和是逆运算
假设ai=b1+b2+b3+···bi
则称bi为ai的差分

一维

在这里插入图片描述

实用价值:
使一段区间内同时进行某一操作 且只需要o(1)的时间

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

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

int main()
{
	scanf("%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;
		cin>>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++)cout<<b[i]<<endl;



}

二维:
差分矩阵

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

insert(int x1,int y1,int x2,int y2,int c)
{
	b[x1][y1]+=c;
	b[x1][y1+1]-=c;
	b[x2][y2+1]-=c;
	b[x2+1][y2+1]+=c;
}

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]);
	}
	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++)
			cout<<b[i][j]<<endl;



}



离散化(区间无限大版 差分)

特点 范围很大 具体数值稀疏

//思路:
//将c加入x位置 定义pair
//用二分找到x 再加入c 
//询问:前缀和

typedef pair<int,int> PII;
vector<PII> query,add;
vector<int> ans;
int n,m;
int a[N],b[N];


int main()
{
	cin>>n>>m;
	for(int i=0;i<n;i++)
	{	
		int x,c;
		cin>>x>>c;
		add.push_back({x,c});
		ans.push_back(x);
	}
	for(int i=0;i<m;i++)
	{
		int l,r;
		cin>>l>>r;
		query.push_back({l,r});
		
		ans.push_back(l);
		ans.push_back(r);
	}
	sort(ans.begin(),ans.end());
	ans.erase(unique(ans.begin(),ans.end()),ans.end());
	
	//处理插入 
	for(auto item:add){
		int x=find(item.first);
		a[x]+=item.second; 
	}
	
	//预处理前缀和
	for(int i=0;i<=ans.size();i++)
	{
		b[i]=b[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;

双指针 算法 模板

for(int i=0,j=0;i<=n;i++){
while(i<j && check(i,j))//左右各一个指针且满足某种性质
{
j++;
//每道题的具体逻辑
}
}

一般思路:
先考虑朴素做法 o(n^2)
再优化作法

最长连续不重复子序列

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

双指针 另一种题型

在这里插入图片描述

先用暴力作法 看是否有单调性
如有 则可将时间复杂度降至一维

在这里插入图片描述

判断子序列

概念:子序列 可以不连续地出现在母序列中
同样使用双指针 判断条件:a中的元素是否出现在b中 出现则移动a中的指针 未出现则一直移动b
在这里插入图片描述

二进制中1的个数

打印出 某个数的二进制数
在这里插入图片描述

	int x;
	cin>>x;
	
	int res=0;
	while(x)x-=lowbit(x),res++;
	
	cout<<res<<endl;

总结

在这里插入图片描述
y总的基础算法就到此结束啦!我们下一章节见~~我爱学习算法嘿嘿(づ ̄3 ̄)づ╭❤~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值