[FROM WOJ]#2445 普通平衡树

本文介绍了Splay树的基本概念、旋转操作以及在信息学竞赛中的应用,包括插入、删除、查询等操作,并提供了样例输入和输出,强调了Splay树在平衡二叉查找树中的优势和操作复杂度。
摘要由CSDN通过智能技术生成

#2445 普通平衡树

输入
您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:
插入x数
删除x数(若有多个相同的数,因只删除一个)
查询x数的排名(若有多个相同的数,因输出最小的排名)
查询排名为x的数
求x的前驱(前驱定义为小于x,且最大的数)
求x的后继(后继定义为大于x,且最小的数)

输出
第一行为n,表示操作的个数,下面n行每行有两个数opt和x,opt表示操作的序号(1<=opt<=6)

样例输入
8
1 10
1 20
1 30
3 20
4 2
2 10
5 25
6 -1

样例输出
2
20
20
20

提示
n<=100000 所有数字均在-10^7 到10^7内

SOL
Splay模板题。
首先,splay是基于BST得到的一种伸展树。

BST,即Binary Search Tree(二叉查找树),满足左儿子权值<当前节点权值<右儿子权值。

二叉查找树能够支持多种动态集合操作,只要所维护的数据集合存在偏序关系(简单来
说就是定义了小于等于)。因此,在信息学竞赛中,二叉查找树起着非常重要的作用,它可
以被用来表示有序集合、建立索引或优先队列等。我们也常常利用它动态地维护一个有序数
集,利用二叉查找树找到新加入序列中的数插入的位置。

BST本身可以支持删除、修改、查询等诸多操作,但是复杂度完全由树的深度决定,因此容易被卡。而Splay是基于BST得到的一种平衡树,它可以通过旋转操作来使均摊复杂度达到 O ( l o g N ) O(logN) O(logN)

旋转分成左旋和右旋,代码里是将这两种操作合并到了一起的。

Splay伸展操作
1.节点 x 的父节点 y 是根节点。这时,如果 x 是 y 的左孩子,我们进行一次 Zig
(右旋)操作;如果 x 是 y 的右孩子,则我们进行一次 Zag(左旋)操作。经过旋转,x 成
为二叉查找树 S 的根节点,调整结束。

2.节点x 的父节点y 不是根节点,y 的父节点为z,且x 与y 同时是各自父节点
的左孩子或者同时是各自父节点的右孩子。这时,我们进行一次Zig-Zig操作或者Zag-Zag操作。

3.节点x的父节点y不是根节点,y的父节点为z,x与y中一个是其父节点的左孩子
而另一个是其父节点的右孩子。这时,我们进行一次Zig-Zag操作或者Zag-Zig 操作。

关于修改、查询的操作
(1) find(x,S):判断元素x是否在伸展树S表示的有序集中。首先,访问根节点,如果x比
根节点权值小则访问左儿子;如果x比根节点权值大则访问右儿子;如果权值相等,则说明x
在树中;如果访问到空节点,则x不在树中。如果x在树中,则再执行Splay(x,S)调整伸展树。

(2) insert(x,S):将元素x插入伸展树S表示的有序集中。首先,访问根节点,如果x比根节
点权值小则访问左儿子;如果x比根节点权值大则访问右儿子;如果访问到空节点t,则把x插
入该节点,然后执行Splay(t,S)。

(3)merge(S1,S2):将两个伸展树S1与S2合并成为一个伸展树。其中S1的所有元素都小于S2
的所有元素。首先,我们找到伸展树S1中最大的一个元素x,再通过Splay(x,S1)将x调整到伸
展树S1的根。然后再将S2作为x节点的右子树。这样,就得到了新的伸展树S。

(4) delete(x,S):把节点x从伸展树表示的有序集中删除。首先,执行Splay(x,S)将x旋转至
根节点。然后取出x的左子树S1和右子树S2,执行merge(S1,S2)把两棵子树合并成S。

Besides,我们还可以用Splay来维护序列,而有时我们需要对序列上某个区间进行操作,这种情况
下,Splay还能支持提取区间操作。比如我们要对[l,r]进行操作,则我们在Splay
中把l -1对应的节点Splay到根,把 r +1Splay到根的右儿子处,之后 r +1的左儿子及它左儿
子的子树,实际上就是区间[l,r]所对应的平衡树。

在Splay上我们也可以类似线段树,打上lazy-tag标记,表示对这个Splay子树中的所有节
点同时进行某些操作。利用标记下放操作,我们只要保证访问某个节点时,根到它路径上没
有标记存在即可保证正确性。

代码:
结构体数组版:

#include<bits/stdc++.h>
#define f(p) t[p].f
#define lc(p) t[p].lc
#define rc(p) t[p].rc
#define v(p) t[p].v
#define siz(p) t[p].siz
#define N 100005
using namespace std;
inline int rd(){
   
	static char ch=0;int register data=0,w=1;
	while(!isdigit(ch)&&ch!='-')ch=getchar();
	if(ch=='-')w=-1,ch=getchar();
	while(isdigit(ch))data=(data<<1)+(data<<3)+ch-'0',ch=getchar();
	return data*w;
}
int rt,tot,m[N];
struct Splay_Tree{
   int f,lc,rc,v,siz;}t[N];
inline int get(int p){
   return rc(f(p))==p;}
inline void update(int p){
   siz(p)=siz(lc(p))+siz(rc(p))+m[p];} 
inline void rotate(int x){
   //旋转
	int register y=f(x),z=f(y),d=get(x),d1=get(y);
	if(d)rc(y)=lc(x),f(rc(y))=y,lc(x)=y;
	else lc(y)=rc(x),f(lc(y))=y,rc(x)=y;
	f(y)=x,f(x)=z;
	if(z){
   if(d1)rc(z)=x;else lc(z)=x;}
	update(x),update(y);
}
inline void splay(int p,int S){
   //伸展
	while(f(p)!=S){
   
		if(f(f(p))!=S){
   
			if(get(p)==get(f(p)))rotate(f(p));
			else rotate(p);
		}
		else
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值