5.17 普通平衡树 splay算法

5.17 普通平衡树

二叉查找树,左小右大,中序遍历是有序的(升序),快速查找插入删除某一个节点

视频

161 普通平衡树 Splay_哔哩哔哩_bilibili

 

#include<bits/stdc++.h>
//节点信息的定义
//旋转操作,伸展操作
//查找操作
//插入,删除,查询
//O(logn) 
using namespace std;
const int N = 100050;
struct node{
	int s[2];//左右儿子 
	//s[0]左儿子,s[1]右儿子 
	int p;//父亲 
	int v;//节点权值 
	int cnt;//权值出现次数 
	int size;//子树大小 
	void init(int p1,int v1){
		p=p1,v=v1;
		cnt=size=1;
	}
	
}tr[N];//
int root;//根节点编号 
int idx;//节点个数
void pushup(int x){
	tr[x].size=tr[tr[x].s[0]].size+tr[tr[x].s[1]].size+tr[x].cnt;
} 
//保序且信息正确 
void rotate(int x){
	int y=tr[x].p,z=tr[y].p;
	int k = tr[y].s[1]==x;
	//k==1右;k==0左 
	tr[y].s[k]=tr[x].s[k^1];
	tr[tr[x].s[k^1]].p=y;
	
	tr[x].s[k^1]=y;
	tr[y].p=x;
	
	tr[z].s[tr[z].s[1]==y]=x;
	tr[x].p=z;
	
	pushup(y),pushup(x);
}

//伸展操作
//访问一个x,就把x旋转到根节点
//k>0,把x转到k下面;
//k=0,把x转到根 
void splay(int x,int k){
	while(tr[x].p!=k){
		int y=tr[x].p,z=tr[y].p;
		if(z!=k){
			(tr[y].s[0]==x)^(tr[z].s[0]==y)?rotate(x):rotate(y); 
		}
		rotate(x);
	}
	if(k==0)root=x;
} 
void find(int v){//找到v并转到根 
	int x=root;
	while(tr[x].s[v>tr[x].v]&&v!=tr[x].v){
		x=tr[x].s[v>tr[x].v];
	}
	splay(x,0);
} 
//找前驱,返回节点编号 
int get_pre(int v){//前驱 
	find(v);
	int x=root;
	if(tr[x].v<v)return x;
	x=tr[x].s[0];
	while(tr[x].s[1]) x=tr[x].s[1];//左子树最大的 
	return x;
}
int get_suc(int v){
	find(v);
	int x=root;
	if(tr[x].v>v)return x;
	x=tr[x].s[1];
	while(tr[x].s[0])x=tr[x].s[0];//右树最小的
	return x; 
}
//删除
void del(int v){
	int pre=get_pre(v);
	int suc=get_suc(v);
	splay(pre,0),splay(suc,pre);
	int del=tr[suc].s[0];
	if(tr[del].cnt>1)tr[del].cnt--,splay(del,0);
	else tr[suc].s[0]=0,splay(suc,0); 
}
//排名
//查数v的排名 
int get_rank(int v){
	find(v);
	return tr[tr[root].s[0]].size;
} 

//数值
//查询排名是k的数值 
int get_val(int k){
	int x=root;
	while(1){
		int y=tr[x].s[0];
		if(tr[y].size+tr[x].cnt<k){
			k-=tr[y].size+tr[x].cnt;
			x=tr[x].s[1];
		}
		else{
			if(tr[y].size>=k)x=tr[x].s[0];
			else break;
		}
	}
	splay(x,0);
	return tr[x].v;
} 

//插入函数,插入v
void insert(int v){
	int x=root,p=0;
	while(x&&tr[x].v!=v)
		p=x,x=tr[x].s[v>tr[x].v];
	if(x) tr[x].cnt++;
	else{
		x=++idx;
		tr[p].s[v>tr[p].v]=x;
		tr[x].init(p,v);
	}
	splay(x,0);
} 

int main(){
	insert(-1e9);insert(1e9);
	int n;
	scanf("%d",&n);
	int op,x;
	while(n--){
		scanf("%d%d",&op,&x);
		if(op==1)insert(x);
		if(op==2)del(x);
		if(op==3)printf("%d\n",get_rank(x));
		if(op==4)printf("%d\n",get_val(x+1));
		if(op==5)printf("%d\n",tr[get_pre(x)].v);
		if(op==6)printf("%d\n",tr[get_suc(x)].v); 
	}
	return 0;
}

线段树套平衡树

线段树每个节点对应一个区间,在每个节点上构建一颗平衡树,维护该区间内的序列

平衡树可以使用splay

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值