——深深感谢你——|

_yolanda

园龄:3年7个月粉丝:11关注:15

2022-02-15 14:29阅读: 181评论: 0推荐: 0

Splay入门(平衡树)

一定要放在最前面的:

超详细

同上

+1

Splay 一个支持功能和 Treap 差不多的东西,只是更高级(有六种旋转操作)

既然是一种平衡树,那就具有 BST 性质

模板


Update

void update(int p){
tr[p].size=tr[tr[p].son[0]].size+tr[tr[p].son[1]].size+tr[p].cnt;
//更新子树大小用于查询排名 ↑
}

Rotate

基本旋转操作

Treap 左旋和右旋操作的合并操作:

  • X变到原来Y的位置

  • Y变成了 X原来在Y的 相对的那个儿子

  • Y的非X的儿子不变 X的 X原来在Y的 那个儿子不变

  • X的 X原来在Y的 相对的 那个儿子 变成了 Y原来是X的那个儿子

void rotate(int x){
int y=tr[x].fa,z=tr[y].fa,k=(tr[y].son[1]==x);
//y是x的父亲,z是爷爷
//k用于判断x是y的左儿子还是右儿子;0:左,1:右
tr[z].son[tr[z].son[1]==y]=x;
tr[x].fa=z;
//x顶替y的位置 ↑
tr[y].son[k]=tr[x].son[k^1];
tr[tr[x].son[k^1]].fa=y;
//改变x的另一个儿子 ↑
tr[x].son[k^1]=y;
tr[y].fa=x;
//更改x,y的关系 ↑
update(y),update(x);//记得修改
}

Spaly

Treap 多出的旋转操作

单纯对 X 旋转两次后仍然有一条链的存在,还是可能会被卡
Ec868.pngEcAbU.png

所以才有了 Splay 操作


两种情况:

  • X和Y分别是Y和Z的同一个儿子

  • X和Y分别是Y和Z不同的儿子

第一种情况:先转Y再转X

第二种情况:转两次X


另外两种情况:

不存在Z,即Y是树的根,只需要对X进行一次旋转即可


void splay(int x,int goal){//将x转为goal的儿子
while(tr[x].fa!=goal){
int y=tr[x].fa,z=tr[y].fa;
if(z!=goal)//y已经是目标节点的儿子,只需要转一次
((tr[z].son[0]==y)^(tr[y].son[0]==x))?rotate(x):rotate(y);
//在同一侧转y,否则转x
rotate(x);//最后转的都是x
}
if(goal==0) root=x;//0是根的父亲,记得换根
}

Find

查找后根就是要找的节点

void fi(int x){
int u=root;
if(!u) return;//树为空
while(tr[u].son[x>tr[u].val] && x!=tr[u].val)
u=tr[u].son[x>tr[u].val];//进入相应节点
splay(u,0);//此时u为查找值的编号,并旋转到根
}

Insert

Treap 的差不多

void insert(int x){
int fa=0,u=root;//fa是要插入节点的父节点
while(u && x!=tr[u].val){
fa=u;
u=tr[u].son[x>tr[u].val];
}
if(u) tr[u].cnt++;//存在直接累加值就好了
else{
u=++tot;
if(fa) tr[fa].son[x>tr[fa].val]=u;
tr[u].son[1]=tr[u].son[0]=0;
tr[u].fa=fa,tr[u].val=x,tr[u].cnt=1,tr[u].size=1;
}
splay(u,0);
//一定要转到根保证平衡。前面改了子树大小所以借此update
}

前驱&后继

int nxt(int x,bool k){//1为后继,0为前驱
fi(x);
int u=root;
if(tr[u].val>x && k) return u;
if(tr[u].val<x && !k) return u;
u=tr[u].son[k];
while(tr[u].son[k^1]) u=tr[u].son[k^1];
return u;
}

Remove

void remove(int x){
int la=nxt(x,0),nex=nxt(x,1);
splay(la,0),splay(nex,la);
//将前驱旋转到根节点,后继旋转到根节点下面
int del=tr[nex].son[0];
if(tr[del].cnt>1){
tr[del].cnt--;
splay(del,0);//修改子树大小
}
else tr[nex].son[0]=0;//直接删
}

找第 k

比较重要,还是附个代码吧

int kth(int x){//查第k大
int u=root;
if(tr[u].size<x) return 0;
while(1){
int y=tr[u].son[0];
if(x>tr[y].size+tr[u].cnt){
x-=tr[y].size+tr[u].cnt;
u=tr[u].son[1];
}
else{
if(tr[y].size>=x) u=y;
else return u;
}
}
}

完整代码
#include<bits/stdc++.h>
using namespace std;
#define INF 20000000
const int N=1e5+5;
struct splay_tree{
int fa,son[2],cnt,val,size;
}tr[N];
int root,tot;
inline void update(int p){
tr[p].size=tr[tr[p].son[0]].size+tr[tr[p].son[1]].size+tr[p].cnt;
}
inline void rotate(int x){
int y=tr[x].fa,z=tr[y].fa,k=(tr[y].son[1]==x);
tr[z].son[tr[z].son[1]==y]=x;
tr[x].fa=z;
tr[y].son[k]=tr[x].son[k^1];
tr[tr[x].son[k^1]].fa=y;
tr[x].son[k^1]=y;
tr[y].fa=x;
update(y),update(x);
}
inline void splay(int x,int goal){
while(tr[x].fa!=goal){
int y=tr[x].fa,z=tr[y].fa;
if(z!=goal)
((tr[z].son[0]==y)^(tr[y].son[0]==x))?rotate(x):rotate(y);
rotate(x);
}
if(goal==0) root=x;
}
inline void fi(int x){
int u=root;
if(!u) return;
while(tr[u].son[x>tr[u].val] && x!=tr[u].val)
u=tr[u].son[x>tr[u].val];
splay(u,0);
}
inline void insert(int x){
int fa=0,u=root;
while(u && x!=tr[u].val){
fa=u;
u=tr[u].son[x>tr[u].val];
}
if(u) tr[u].cnt++;
else{
u=++tot;
if(fa) tr[fa].son[x>tr[fa].val]=u;
tr[u].son[1]=tr[u].son[0]=0;
tr[u].fa=fa,tr[u].val=x,tr[u].cnt=1,tr[u].size=1;
}
splay(u,0);
}
inline int nxt(int x,bool k){
fi(x);
int u=root;
if(tr[u].val>x && k) return u;
if(tr[u].val<x && !k) return u;
u=tr[u].son[k];
while(tr[u].son[k^1]) u=tr[u].son[k^1];
return u;
}
inline void remove(int x){
int la=nxt(x,0),nex=nxt(x,1);
splay(la,0),splay(nex,la);
int del=tr[nex].son[0];
if(tr[del].cnt>1){
tr[del].cnt--;
splay(del,0);
}
else tr[nex].son[0]=0;
}
inline int rk(int p,int x){
if(!p) return 0;
if(x==tr[p].val)
return tr[tr[p].son[0]].size+1;
if(x>tr[p].val)
return rk(tr[p].son[1],x)+tr[tr[p].son[0]].size+tr[p].cnt;
return rk(tr[p].son[0],x);
}
inline int kth(int x){
int u=root;
if(tr[u].size<x) return 0;
while(1){
int y=tr[u].son[0];
if(x>tr[y].size+tr[u].cnt){
x-=tr[y].size+tr[u].cnt;
u=tr[u].son[1];
}
else{
if(tr[y].size>=x) u=y;
else return u;
}
}
}
int main(){
int n;
cin>>n;
insert(-INF),insert(INF);
while(n--){
int opt,x;
scanf("%d%d",&opt,&x);
if(opt==1) insert(x);
if(opt==2) remove(x);
if(opt==3) cout<<rk(root,x)-1<<endl;
if(opt==4) cout<<tr[kth(x+1)].val<<endl;
if(opt==5) cout<<tr[nxt(x,0)].val<<endl;
if(opt==6) cout<<tr[nxt(x,1)].val<<endl;
}
return 0;
}

本文作者:_yolanda

本文链接:https://www.cnblogs.com/yolanda-yxr/p/15896370.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   _yolanda  阅读(181)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起
  1. 1 化身孤岛的鲸 周深
  2. 2 像鸟儿一样(Live) 周深
  3. 3 Rubia 周深
  4. 4 痕迹 周深
化身孤岛的鲸 - 周深
00:00 / 00:00
An audio error has occurred, player will skip forward in 2 seconds.

化身孤岛的鲸 - 周深

词:沃特艾文儿

曲:徐浩

编曲:唐汉霄

制作人:唐汉霄

配唱制作人:徐威

弦乐编配:欧阳菁宇

和声编配录制:杨子轩

人声录音棚:V-Studio

混音/母带:周天澈

出品:周深工作室

发行:百纳娱乐/云海娱乐

我是只化身孤岛的蓝鲸

有着最巨大的身影

鱼虾在身侧穿行

鱼虾在身侧穿行

也有飞鸟在背上停

也有飞鸟在背上停

我路过太多太美的奇景

我路过太多太美的奇景

如同伊甸般的仙境

而大海太平太静

而大海太平太静

多少故事无人倾听

多少故事无人倾听

我爱地中海的天晴

我爱地中海的天晴

爱西伯利亚的雪景

爱西伯利亚的雪景

爱万丈高空的鹰

爱肚皮下的藻荇

我在尽心尽力地多情

直到那一天

你的衣衫破旧

你的衣衫破旧

而歌声却温柔

陪我漫无目的的四处漂流

我的背脊如荒丘

而你却微笑摆首

把它当成整个宇宙

你与太阳挥手

也同海鸥问候

陪我爱天爱地的四处风流

只是遗憾你终究

无法躺在我胸口

欣赏夜空最辽阔的不朽

把星子放入眸

我是只化身孤岛的蓝鲸

我是只化身孤岛的蓝鲸

有着最巨大的身影

鱼虾在身侧穿行

鱼虾在身侧穿行

也有飞鸟在背上停

也有飞鸟在背上停

我有着太冷太清的天性

对天上的她动过情

而云朵太远太轻

而云朵太远太轻

辗转之后各安天命

辗转之后各安天命

我未入过繁华之境

未听过喧嚣的声音

未见过太多生灵

未有过滚烫心情

所以也未觉大洋正中

有多么安静

你的衣衫破旧

而歌声却温柔

陪我漫无目的的四处漂流

我的背脊如荒丘

而你却微笑摆首

把它当成整个宇宙

你与太阳挥手

也同海鸥问候

陪我爱天爱地的四处风流

只是遗憾你终究

无法躺在我胸口

欣赏夜空最辽阔的不朽

把星子放入眸

你的指尖轻柔

你的指尖轻柔

抚摸过我所有

风浪冲撞出的丑陋疮口

你眼中有春与秋

胜过我见过爱过

的一切山川与河流

曾以为我肩头

是那么的宽厚

足够撑起海底那座琼楼

而在你到来之后

它显得如此清瘦

我想给你能奔跑的岸头

让你如同王后

让你如同王后