数据结构模板总结

数据结构注意端点、初始化、和数据范围

并查集

基础并查集:
using namespace std;
int bin[N]
int findd(int x){//查找根节点 
    int next=x;
    while(bin[next]!=next)
        next=bin[next];
    return next;
}
void merge(int x,int y){//合并 
    int fx,fy;
    fy=findd(y);
    fx=findd(x);
    if(fx!=fy)
    bin[fx]=fy; 
} 
int main(){
            for( int i=1;i<=n;i++)
        bin[i]=i;//注意集合的初始化
}
并查集的合并优化:
using namespace std;
int bin[N]
int height[N]//注意需要初始化为0
int findd(int x){//查找根节点 
    int next=x;
    while(bin[next]!=next)
        next=bin[next];
    return next;
}
void merge(int x,int y){//合并的优化
    y=findd(y);
    x=findd(x);
    if(height[x]==height[y]){
        height[x] = height[y] +1;//x的树高+1
        bin[y]=x;//y向x合并
} 
else{
    if(height[x]<height[y]) bin[x]=y;
//如果y比较高,就将x合并在y上,同时y的树高不变
	else bin[y]=x;
	}
} 
int main(){
            for( int i=1;i<=n;i++)
        bin[i]=i;//注意集合的初始化
}
并查集查找的优化:
int find(int x){
    if( x!= s[x] ) s[x]=find(s[x]);
//在查找的路径上把经过节点的父节点改为根节点
    return s[x];
}
//非递归方法见黑书

树状数组

树状数组的功能是单点修改和区间查询

int a[100100];
int c[100100];
int n;
//a为原数组,c为树状数组,下同
void creat(){
	for( int i=1;i<=n;i++){
		for( int j=0;j<lowbit(i);j++){
			c[i]+=a[i-j];
		}
	}
}
int query( int x){
	int sum=0;
	while(x>0){
		sum+=c[x];
		x-=lowbit(x);
	}
	return sum;
}
void update( int i,int val){
	while(i<=n){
		c[i]+=val;
		i+=lowbit(i);
	}
}

线段树:

线段树的功能是区间修改,单点查询,区间查询
感觉位运算和普通乘法在运行效率上没有明现区别

没有lazy标记的线段树

***输入为a[N]
***
***
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define N 100100
struct node {
	int l,r,val;
}tr[N*4];
int a[N];
int creat(int rt,int l,int r){
	tr[rt].l=l,tr[rt].r=r;
	if(l==r) {
		tr[rt].val=a[l];
		return a[l];
	}
	int mid=(l+r)/2,res=0;
	res+=creat(rt*2,l,mid);
	res+=creat(rt*2+1,mid+1,r);
	tr[rt].val=res;
	return res;
}
int qq( int rt,int L,int R){
	int l=tr[rt].l,r=tr[rt].r;
	if(l>=L&&r<=R) return tr[rt].val;
	if(r<L||l>R) return 0;//有的模板会省略这句,个人感觉不妥
	int mid=(l+r)/2;
	int res=0;
	if(L<=mid) res+=qq(rt*2,L,R);
	if(R>mid) res+=qq(rt*2+1,L,R);
	return res; 
}
void up(int val,int pos,int rt){
	int l=tr[rt].l,r=tr[rt].r;
	if(l==r) tr[rt].val=val;
	if(l>pos||r<pos) return ;
	int mid=(l+r)/2;
	if(pos<=mid) up(val,pos,rt*2);
	else up(val,pos,rt*2+1);
	tr[rt].val=tr[rt*2].val+tr[rt*2+1].val;
}
void mul( int val,int pos,int rt){//将某一个数乘上val倍 
	int l=tr[rt].l,r=tr[rt].r;
	if(l==r) tr[rt].val*=val;
	if(l>pos||r<pos) return ;
	int mid=(l+r)/2;
	if(pos<=mid) up(val,pos,rt*2);
	else up(val,pos,rt*2+1);
	tr[rt].val=tr[rt*2].val+tr[rt*2+1].val;
}
int main(){
	int n,m;
	cin>>n>>m;
	for( int i=1;i<=n;i++){
		cin>>a[i];
	}
	creat(1,1,n);
}


有lazy标记线段树
**lazy标记的含义是:**当前节点已经更新,但是子节点还差lazy没有更新

struct node{
	int l,r,val;
	int lz;
}tr[N<<2];//开四倍空间
void creat(int rt,int l,int r){
	tr[rt].l=l;tr[rt].r=r;
	tr[rt].lz=xxx;//初始化lz标记
	if(l==r) {
		tr[rt].val=xxx;//维护叶子结点
		return;
	}
	int mid=(l+r)/2;
	creat(rt*2,l,mid);
	creat(rt*2+1,mid+1,r);
	tr[rt].val=xxx;//维护根节点
	return ;
}
void pushdown(int rt){
	if(tr[rt].lz==xxx) return ;//如果lz标记不存在
	int l=tr[rt].l,r=tr[rt].r;
	int mid=(l+r)/2;
	int ln=mid-l+1,rn=r-mid;
	tr[rt*2].val=min(tr[rt].lz,tr[rt*2].val);//维护左子树
	tr[rt*2+1].val=min(tr[rt].lz,tr[rt*2+1].val);//维护右子树
	tr[rt*2].lz=min(tr[rt*2].lz,tr[rt].lz);//向左子树传递lz标记
	tr[rt*2+1].lz=min(tr[rt*2+1].lz,tr[rt].lz);//向右子树传递lz标记
	tr[rt].lz=inf;//清除lz标记
	return ;
}
void up_add(int rt,int L,int R,int val){
	int l=tr[rt].l,r=tr[rt].r;
	if(L>r||R<l) return ;
	if(l==r) {
		tr[rt].val=xxx;//维护叶子结点
		return ;
	}
	if(l>=L&&r<=R) {
		int len=r-l+1;
		tr[rt].val=min(tr[rt].val,val);//维护val
		tr[rt].lz=min(tr[rt].lz,val);//更新lz标记
		return ;
	}
	pushdown(rt);
	int mid=(l+r)/2;
	//
	if(mid>=L) up_add(rt*2,L,R,val);
	if(mid<R) up_add(rt*2+1,L,R,val);
	tr[rt].val=min(tr[rt*2].val,tr[rt*2+1].val);
}
int qq(int rt,int L,int R){
	int l=tr[rt].l,r=tr[rt].r;
	if(l>R||r<L) return xxx;
	if(l==r) return tr[rt].val;
	pushdown(rt);
	if(l>=L&&r<=R) return tr[rt].val;
	int mid=(l+r)/2;
	//维护返回值
	int res=xxx;
	if(L<=mid) res=min(qq(rt*2,L,R),res);
	if(R>mid) res=min(qq(rt*2+1,L,R),res);
	return res;
}
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define N 100100
ll mol;
int a[100100];
struct node{
	int l,r,lz_add,lz_mul;
	ll val;
}tr[N<<2];
void creat(int rt,int l,int r){
	tr[rt].l=l;tr[rt].r=r;
	tr[rt].lz_add=0;tr[rt].lz_mul=1;
	if(l==r) {
		tr[rt].val=a[l];
		return;
	}
	int mid=(l+r)/2;
	creat(rt*2,l,mid);
	creat(rt*2+1,mid+1,r);
	tr[rt].val=tr[rt*2].val+tr[rt*2+1].val;
	return ;
}
void pushdown(int rt){
	if(tr[rt].lz_add==0&&tr[rt].lz_mul==1) return ;
	int l=tr[rt].l,r=tr[rt].r;
	int mid=(l+r)/2;
	int ln=mid-l+1,rn=r-mid;
	if(tr[rt].lz_add!=0){
		tr[rt*2].val+=tr[rt].lz_add*ln;
		tr[rt*2+1].val+=tr[rt].lz_add*rn;
		tr[rt*2].lz_add+=tr[rt].lz_add;
		tr[rt*2+1].lz_add+=tr[rt].lz_add;
		tr[rt].lz_add=0;
	}
	if(tr[rt].lz_mul!=1) {
		tr[rt*2].val*=tr[rt].lz_mul%mol;
		tr[rt*2+1].val*=tr[rt].lz_mul%mol;
		tr[rt*2].lz_mul*=tr[rt].lz_mul;
		tr[rt*2+1].lz_mul*=tr[rt].lz_mul;
		tr[rt].lz_mul=1;
	}
	return ;
}
void up_add(int rt,int L,int R,int val){
	int l=tr[rt].l,r=tr[rt].r;
	if(L>r||R<l) return ;	
	if(l==r) {
		tr[rt].val+=val;return ;
	}
	if(l>=L&&r<=R) {
		int len=r-l+1;
		tr[rt].val+=len*val;
		tr[rt].lz_add+=val;
		return ;
	}
	pushdown(rt);
	int mid=(l+r)/2;
	if(mid>=L) up_add(rt*2,L,R,val);
	if(mid<R) up_add(rt*2+1,L,R,val);
	tr[rt].val=tr[rt*2].val+tr[rt*2+1].val;
}
void up_mul( int rt,int L,int R,int val){
	int l=tr[rt].l,r=tr[rt].r;
	if(L>r||R<l) return ;	
	if(l==r) {
		tr[rt].val*=val;
		tr[rt].val%=mol;
		return ;
	}
	if(l>=L&&r<=R) {
		int len=r-l+1;
		tr[rt].val*=val;
		tr[rt].lz_mul*=val;
		tr[rt].val%=mol;
		tr[rt].lz_mul%=mol;
		return ;
	}
	pushdown(rt);
	int mid=(l+r)/2;
	if(mid>=L) up_mul(rt*2,L,R,val);
	if(mid<R) up_mul(rt*2+1,L,R,val);
	tr[rt].val=tr[rt*2].val+tr[rt*2+1].val; 
}
ll qq(int rt,int L,int R){
	int l=tr[rt].l,r=tr[rt].r;
	if(l>R||r<L)return 0;
	if(l==r) return tr[rt].val;
	pushdown(rt);
	if(l>=L&&r<=R) return tr[rt].val;
	int mid=(l+r)/2;
	ll res=0;
	if(L<=mid) res+=qq(rt*2,L,R);
	if(R>mid) res+=qq(rt*2+1,L,R);
	res%=mol;
	return res;
}

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define N 100100
ll mol;
***输入数据是a
***需要区间加法修改1
***需要区间乘法修改2
***需要多个重叠区间区间最大值3
***需要区间最小值4
***数据范围修改N,注意ll
int a[N];
struct node{
	int l,r;
    1111//int lz_add;
    2222//int lz_mul;
    3333//int maxx;int lz_maxx;
    4444//int minn;int lz_minn;
	ll val;
}tr[N<<2];
void creat(int rt,int l,int r){
	tr[rt].l=l;tr[rt].r=r;
	1111//tr[rt].lz_add=0;
    2222//tr[rt].lz_mul=1;
    3333//tr[rt].lz_maxx=-inf;
    4444//tr[rt].lz_minn=inf;
	if(l==r) {
		11112222//tr[rt].val=a[l];
        3333//tr[rt].maxx=a[l];
        4444//tr[rt].minn=a[l];
		return;
	}
	int mid=(l+r)/2;
	creat(rt*2,l,mid);
	creat(rt*2+1,mid+1,r);
	11112222//tr[rt].val=tr[rt*2].val+tr[rt*2+1].val;
    3333//tr[rt].minn=max(tr[rt*2].maxx,tr[rt*2+1].maxx);
    4444//tr[rt].minn=min(tr[rt*2].minn,tr[rt*2+1].minn);
	return ;
}
void pushdown(int rt){
    1111// if(tr[rt].lz_add==0) return ;
    2222// if(tr[rt].lz_mul==1) return ;
    3333// if(tr[rt].lz_maxx==-inf) return ;
    4444// if(tr[rt].lz_minn==inf) return ;
	int l=tr[rt].l,r=tr[rt].r;
	int mid=(l+r)/2;
	int ln=mid-l+1,rn=r-mid;
    1111
	// 	tr[rt*2].val+=tr[rt].lz_add*ln;
	// 	tr[rt*2+1].val+=tr[rt].lz_add*rn;
	// 	tr[rt*2].lz_add+=tr[rt].lz_add;
	// 	tr[rt*2+1].lz_add+=tr[rt].lz_add;
	// 	tr[rt].lz_add=0;

    2222
	// 	tr[rt*2].val*=tr[rt].lz_mul%mol;
	// 	tr[rt*2+1].val*=tr[rt].lz_mul%mol;
	// 	tr[rt*2].lz_mul*=tr[rt].lz_mul;
	// 	tr[rt*2+1].lz_mul*=tr[rt].lz_mul;
	// 	tr[rt].lz_mul=1;
	
    3333
    	// tr[rt*2].maxx=max(tr[rt].lz_maxx,tr[rt*2].maxx);
		// tr[rt*2+1].maxx=max(tr[rt].lz_maxx,tr[rt*2+1].maxx);
		// tr[rt*2].lz_maxx=max(tr[rt*2].lz_maxx,tr[rt].lz_maxx);
		// tr[rt*2+1].lz_maxx=max(tr[rt*2+1].lz_maxx,tr[rt].lz_maxx);
		// tr[rt].lz_maxx=-inf;

    4444
    	// tr[rt*2].minn=min(tr[rt].lz_minn,tr[rt*2].minn);
		// tr[rt*2+1].minn=min(tr[rt].lz_minn,tr[rt*2+1].minn);
		// tr[rt*2].lz_minn=min(tr[rt*2].lz_minn,tr[rt].lz_minn);
		// tr[rt*2+1].lz_minn=min(tr[rt*2+1].lz_minn,tr[rt].lz_minn);
		// tr[rt].lz_minn=inf;
	return ;
}
void update(int rt,int L,int R,int val){
	int l=tr[rt].l,r=tr[rt].r;
	if(L>r||R<l) return ;	
	if(l==r) {
	如果不是区间重叠而是更新的话需要在本if和下一个if修改
		1111//tr[rt].val+=val;
        2222//tr[rt].val*=val;
        3333//tr[rt].maxx=max(val,tr[rt].maxx);
        4444//tr[rt].minn=min(val,tr[rt].minn);
        return ;
	}
	if(l>=L&&r<=R) {
		int len=r-l+1;
        1111
		// tr[rt].val+=len*val;
		// tr[rt].lz_add+=val;
        2222
    	// 	tr[rt].val*=val;
		// tr[rt].lz_mul*=val;    
        3333
        //tr[rt].maxx=max(tr[rt].maxx,val);
        //tr[rt].lz_maxx=max(tr[rt].lz_maxx,val);
        4444
        //tr[rt].minn=min(tr[rt].minn,val);
        //tr[rt].lz_minn=min(tr[rt].lz_minn,val);
		return ;
	}
	pushdown(rt);
	int mid=(l+r)/2;
	if(mid>=L) update(rt*2,L,R,val);
	if(mid<R) update(rt*2+1,L,R,val);
    1111//tr[rt].val=tr[rt*2].val+tr[rt*2+1].val;
    2222//tr[rt].val=tr[rt*2].val+tr[rt*2+1].val; 
    3333//tr[rt].maxx=maxx(tr[rt*2].maxx,tr[rt*2+1].maxx);
    4444//tr[rt].minn=minn(tr[rt*2].minn,tr[rt*2+1].minn);
}
ll qq(int rt,int L,int R){
	int l=tr[rt].l,r=tr[rt].r;
	if(l>R||r<L){
        1111//return 0;
        2222//return 0;
        3333//return -inf;
        4444//return inf;
    }
	if(l==r){
        1111//return tr[rt].val;
        2222//return tr[rt].val;
        3333//return tr[rt].maxx;
        4444//return tr[rt].minn;
    } 
	pushdown(rt);
	if(l>=L&&r<=R) {
        1111//return tr[rt].val;
        2222//return tr[rt].val;
        3333//return tr[rt].maxx;
        4444//return tr[rt].minn;
    }
	int mid=(l+r)/2;

	1111//ll res=0;
    2222//ll res=0;
    3333//ll res=-inf;
    4444//ll res=inf;
    1111
	// if(L<=mid) res+=qq(rt*2,L,R);
	// if(R>mid) res+=qq(rt*2+1,L,R);
    2222
    // if(L<=mid) res+=qq(rt*2,L,R);
	// if(R>mid) res+=qq(rt*2+1,L,R);
    3333
    // if(L<=mid) res=max(qq(rt*2,L,R),res);
	// if(R>mid) res=max(qq(rt*2+1,L,R),res);
    4444
    // if(L<=mid) res=min(qq(rt*2,L,R),res);
	// if(R>mid) res=min(qq(rt*2+1,L,R),res);
	return res;
}

KMP算法

#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std; 
int p[50100];
char a[50100];
char b[50100];
int la,lb;
void pre(char *b){
	int j=0;p[1]=0;
	for( int i=1;i<lb;i++){//注意这里是从1开始,
	//因为量字符串要错开 
		while(b[i+1]!=b[j+1]&&j>0){
			j=p[j];
		}
		if(b[i+1]==b[j+1]) j++;
		p[i+1]=j;//上面判断的是i+1,所以这里是i+1 
	}
} 
void solve(){
	int j=0;
	for( int i=0;i<lb;i++){
		//注意这里要从0开始,因为在判断的是i+1 
		while(b[i+1]!=a[j+1]&&j){
			j=p[j]; 
		}
		if(b[i+1]==a[j+1]){
			j++;
		}
		if(){
			
		} 
	}
}
int main(){
	while(~scanf("%s%s",a+1,b+1)){
		la=strlen(a+1);
		lb=strlen(b+1);
		memset(p,0,sizeof(p));
		//一般而言p无需初始化,只要不越界就可以 		
		maxx=0;
		pre(a);
		solve();

	}
}

int Pre[100100];
string s,p;
void get_Pre(string p){
	Pre[0]=0,Pre[1]=0;
	for( int i=1;i<b.size();i++){
		int j=Pre[i];
		while(p[i]!=p[j]&&j) j=Pre[j];
		Pre[i+1] = (p[i]==p[j]) ? j+1:0;
	}
} 
int kmp( string s,string p){
	int last=-1;
	get_Pre(p);
	for( int i=0;i<s.size();i++){
		while(j&&s[i]!=p[i]) j=Pre[j];
		if(s[i]==p[j]) j++;
		if(j==p.size()) {
			//匹配成功
		}
	}

}

st表

st表可以求解区间最大值和最小值,不支持动态修改

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define N 200100
int st[N][20];
int bin[20],Log[N];
int main(){
	int n;
	bin[0]=1;
	for( int i=1;i<20;i++) bin[i]=bin[i-1]*2;
	//计算2的i次方 
	Log[0]=-1;
	for( int i=1;i<=N;i++)
	Log[i]=Log[i/2]+1;
	for( int i=1;i<=n;i++){
		st[i][0]=a[i];
	}
	for( int i=1;i<=Log[n];i++){//枚举区间长度 
		for( int j=1;j<=n;j++)//枚举区间开始点 
		if(j+bin[i]-1<=n)
			st[j][i]=min(st[j][i-1],st[j+bin[i-1]][i-1]);
	}
	
	//查询:
	int t=Log[r-l+1];
	printf("%d",min(st[l][t],st[r-bin[t]+1][t])) ;
}

字典树

typedef long long ll;
const int N=1001000;
struct node {
	int cnt;
	int ne[26];
}tr[N];
int cnt=1;
void insert(string s ){
	int p=0;
	for( int i=0;i<s.size();i++){
		int a=s[i]-'a';
		if(tr[p].ne[a]==0) tr[p].ne[a]=cnt++;
		p=tr[p].ne[a];
	}
	tr[p].cnt++;
}
int find(string s){
	int p=0;
	int res=0;
	for( int i=0;i<s.size();i++){
		int a=s[i]-'a';
		if(tr[p].ne[a]==0) return res;
		p=tr[p].ne[a];
		res+=tr[p].cnt;
	}
	return res;
}

ac自动机

#include <bits/stdc++.h>
using namespace std;
const int N=10010,S=55,M=1000010;
struct node{
	int ne[26];
	int cnt=0;
	int fail;
}tr[N*S];
int idx=1;
void insert(string s){
	int p=0;
	for( int i=0;i<s.size();i++){
		int a=s[i]-'a';
		if(tr[p].ne[a]==0) tr[p].ne[a]=idx++;
		p=tr[p].ne[a];
	}
	tr[p].cnt++;
}
queue<int>q;
void bfs(){
	for( int i=0;i<26;i++){
		if(tr[0].ne[i]!=0){
			q.push(tr[0].ne[i]);
		}
	}
	while(!q.empty()){
		int now=q.front();
		q.pop();
		for( int i=0;i<26;i++){
			int p=tr[now].ne[i];
			if(p==0) tr[now].ne[i]=tr[tr[now].fail].ne[i];
			//如果是空结点,指向其父节点的回溯结点
			else{
				tr[p].fail=tr[tr[now].fail].ne[i];
				//如果结点不空,那么就把回溯指针指向其父节点回族结点的相同字母结点
				q.push(p);
			}
		}
	}
}
int main(){
	int t;
	cin>>t;
	while(t--){
		memset(tr,0,sizeof tr);
		idx=1;
		int n;
		cin>>n;
		for( int i=0;i<n;i++){
			string s;
			cin>>s;
			insert(s);
		}
		bfs();
		int res=0;
		string s;
		cin>>s;
		for( int i=0,j=0;i<s.size();i++){
			int a=s[i]-'a';
			j=tr[j].ne[a];

			int p=j;
			while(p){
				res+=tr[p].cnt;
				tr[p].cnt=0;
				p=tr[p].fail;
			}
		}
		cout<<res<<endl;
	}
}

可持久化线段树

绝大多数情况下,可耻就化线段树用于维护某个区间内数字出现的个数,线段树开在下标上,即权值线段树
权值线段树不一定离散化,即使没有的点就空着,留给0结点
相当于线段树套线段树

# 可持久化线段树

```cpp
#include <bits/stdc++.h>
using namespace std;
struct num
{
    int val, pos;
    int val2;
} a[200100];
int b[200100];
struct node
{
    int val;
    node *l, *r;
    node()
    {
        l = NULL;
        r = NULL;
        val = 0;
    }
};

node *h[200100];
node *build(int l, int r)
{ //建立空树
    node *Newnode = new node;
    if (l == r)
        return Newnode;
    int mid = (l + r) / 2;
    Newnode->l = build(l, mid);
    Newnode->r = build(mid + 1, r);
    return Newnode;
}
node *insert(node *old, int l, int r, int val, int pos)
{
    node *Newnode = new node;
    *Newnode = *old; //将老树的数据拷贝,注意指针引用
    if (l == r)
    {
        Newnode->val += val;
        return Newnode;
    }
    int mid = (l + r) / 2;
    if (pos <= mid)
        Newnode->l = insert(Newnode->l, l, mid, 1, pos);
    else
        Newnode->r = insert(Newnode->r, mid + 1, r, 1, pos);
    Newnode->val = Newnode->l->val + Newnode->r->val;
    return Newnode;
}
int q(node *h1, node *h2, int l, int r, int k)
{
    if (l == r)
        return l;
    int mid = (l + r) / 2;
    int cnt = h1->l->val - h2->l->val;
    // cout<<l<<" "<<r<<" "<<mid<<" "<<cnt<<" "<<k<<endl;
    //求区间第k大的数,而且最终返回的是左区间的值,
    if (k <= cnt)
        return q(h1->l, h2->l, l, mid, k);
    //左区间已经有k个值,那么直接在左区间寻找即可
    else
        //左区间不足k个值,还差k - cnt个值,在有区间中再找k - cnt个值
        return q(h1->r, h2->r, mid + 1, r, k - cnt);
}
int cmp(num a, num b)
{
    return a.val < b.val;
}

int main()
{
    int n, m;
    cin >> n >> m;
    for (int i = 1; i <= n; i++)
    {
        cin >> a[i].val;
        a[i].pos = i;
    }
    sort(a + 1, a + 1 + n, cmp);
    for (int i = 1; i <= n; i++)
    {
        b[a[i].pos] = i;
    }
    h[0] = build(0, n);
    for (int i = 1; i <= n; i++)
    {
        h[i] = insert(h[i - 1], 1, n, 1, b[i]);
    }
    while (m--)
    {
        int l, r, k;
        scanf("%d%d%d", &l, &r, &k);
        printf("%d\n", a[q(h[r], h[l - 1], 1, n, k)].val);
    }

    return 0;
}

treap 树

先介绍使用multiset模拟平衡树

multiset<int>se;

se.insert(x);
向平衡树中插入x

auto it=se.find(x);
se.erase(it);
在平衡树中删除一个x

auto minn=se.begin();
minn指向平衡树中的最小值


auto maxx=se.rbegin();
maxx指向平衡树中的最大值

auto it=se.find(x);
it--;
(查找一个在树中存在的数的前驱)
it指向x不等于x的前驱

不等于x的后继结点需要些循环
	auto it=se.find(x);
	while(*it==x) it++;

lower_bound(x)找到大于等于x的数,返回迭代器,x不一定存在
upper_bound(x)找到严格大于x的数,返回迭代器,x不一定存在

#include <bits/stdc++.h>
using namespace std;
const int N=100100,inf=1e8;
int n;
struct node
{
	int l,r;
	int key,val;
	int cnt ,size;
	//size用来寻找排名
}tr[N];
int root ,idx;
void pushup( int p){
	tr[p].size=tr[tr[p].l].size+tr[tr[p].r].size+tr[p].cnt;
	//维护结点的size等于两个子树的size+自己的数值个数
}
int get_node( int key){
	tr[++idx].key=key;
	tr[idx].val=rand();
	tr[idx].cnt=tr[idx].size=1;
	return idx;
}
void zig( int &p) //右旋
//这里要修改p的值,要传递引用
{
	int q=tr[p].l;
	tr[p].l=tr[q].r,tr[q].r=p,p=q;
	pushup(tr[p].r),pushup(p);
}
void zag ( int &p){
	int q=tr[p].r;
	tr[p].r=tr[q].l,tr[q].l=p,p=q;
	pushup(tr[p].l),pushup(p);
}
void build(){
	get_node(-inf),get_node(inf);
	root=1,tr[1].r=2;
	pushup(root);
	if(tr[1].val<tr[2].val) zag(root);
	//两个哨兵一个位于树的最左边,另一个位于树的最右边
}

void insert( int &p,int key){
	if(!p) p=get_node(key);
	else if(tr[p].key==key) tr[p].cnt++;
	else if(tr[p].key>key){
		insert(tr[p].l,key);
		if(tr[tr[p].l].val>tr[p].val) zig(p);
		//先把新节点当作叶子结点插入,然后旋转到适当的位置

	}
	else{
		insert(tr[p].r,key);
		if(tr[tr[p].r].val>tr[p].val) zag(p);
	}
	pushup(p);
}
void remove( int &p,int key){
	//将结点转到叶子结点再删除
	if(!p)return ;
	if(tr[p].key==key){//要删除当前结点
		if(tr[p].cnt>1) tr[p].cnt--;
		else if(tr[p].l||tr[p].r){
			//存在子节点
			if(!tr[p].r||tr[tr[p].l].val>tr[tr[p].r].val){
				//只存在左儿子或者左val大于右val
				zig(p);//右旋
				remove (tr[p].r,key);
			}
			else{
				zag(p);
				remove (tr[p].l,key);
			}
		}
		else p=0;//如果是叶子结点,让父节点指向该节点的指针等于0
		
	}
	else if(tr[p].key>key) remove(tr[p].l,key);
	else remove(tr[p].r,key);
	pushup(p);
}

int get_rank_by_key( int p,int key){
	//通过数值找排名
	if(!p) return 0;
	if(tr[p].key==key) return tr[tr[p].l].size+1;
	if(tr[p].key>key)return get_rank_by_key(tr[p].l,key);
	else
	return tr[tr[p].l].size+tr[p].cnt+get_rank_by_key(tr[p].r,key);
}
int get_key_by_rank(int p,int rank){
	//通过排名找数值
	if(!p) return inf;
	if(tr[tr[p].l].size>=rank) return get_key_by_rank(tr[p].l,rank);
	if(tr[tr[p].l].size+tr[p].cnt>=rank)
	return tr[p].key;
	//左子树个数不够,但是加上当前结点数值又多了
	else
	return get_key_by_rank(tr[p].r,rank-tr[tr[p].l].size-tr[p].cnt);
	//左子树加上当前结点也不够,就在右子树找一些结点补上
}
int get_prev( int p,int key){
	if(!p) return -inf;
	if(tr[p].key>=key) return get_prev(tr[p].l,key);
	//右子树一定没有,在左子树找
	return max(tr[p].key,get_prev(tr[p].r,key));
}
int get_next(int p,int key){
	if(!p) return inf;
	if(tr[p].key<=key) return get_next(tr[p].r,key);
	return min(tr[p].key,get_next(tr[p].l,key));
}
int main(){
	build();
	int n;
	scanf("%d",&n);
	while(n--){
		int op,x;
		scanf("%d%d",&op,&x);
		if(op==1) insert(root,x);
		else if(op==2) remove(root,x);
		else if(op==3) printf("%d\n",get_rank_by_key(root,x)-1);
		else if(op==4) printf("%d\n",get_key_by_rank(root,x+1));
		else if(op==5) printf("%d\n",get_prev(root,x));
		else printf("%d\n",get_next(root,x));
	}
	return 0;
}
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值