fhq-Treap是一种非常优秀的无旋平衡树。下面我们来介绍他的函数原理
我们先介绍他的两个重要函数
1.spilt(分割函数)
我们将整棵树按照值的大小分割成x,y两棵树,x中的值都是小于等于key值的
这样我们就可以通过递归操作来解析,下面看具体代码
void spilt(int p,int key,int &x,int &y){ //按key分裂
if(!p) //分裂完毕赋空值
x=y=0;
else{
if(tr[p].key<=key){ //满足左子树条件,所有值小于key
x=p;
spilt(tr[p].r,key,tr[p].r,y);//递归右边节点
}
else{
y=p;
spilt(tr[p].l,key,x,tr[p].l);//同理,递归左边
}
pushup(p); //无论哪个值取到p,只需要更新p,那就更新完了节点
}
}
重点就是如果当前值小于等于key,那么他的左子树都是小于等于key的,右子树有可能也存在,因此将x=p,再将右子树传递,反之同理,结束点是目前这个是空树
2.merge(合并函数)
我们考虑将两个已经分割好的树合并起来,有几个注意点
递归结束点就是存在一棵树已经合并完了,那么只需要返回x+y就是我们当前所需的根节点值
下面我们先来看代码
int merge(int x,int y){
if(!x||!y)
return x+y;//返回不为0的值
if(tr[x].val>=tr[y].val){ //根据二叉堆的性质
tr[x].r=merge(tr[x].r,y); //x中值都比y中的小
pushup(x);
return x; //父节点是x
}
else{
tr[y].l=merge(x,tr[y].l);
pushup(y);
return y;
}
}
如果左边节点的索引值大于右边,我们又可以知道根据二叉树和二叉堆的共同作用下,x中的树要比y中的树小,但是x又要是根节点,那么y树就要在x的右子树中,反之同理
注意合并完后要更新节点的个数大小
下面我们来分析这两个函数如何完成平衡树的操作
众所周知,平衡树的操作为了获取以下功能
1.insert函数
我们需要将一个新值插入树当中,那么我们可以先将树按这个新值key来切割,左边都是小于等于key,然后我们把x和key生成的节点合并,再把x和y合并就可
int insert(int key){
spilt(root,key,x,y); //按key值分离
root=merge(merge(x,get(key)),y);//合并回来更新root
}
2.remove函数(删除一个节点)
我们先按key分离x,z
再按key-1分离x,y
这样y树中值就全都是key了,然后我们将y中左子树和右子树合并,就成功删除了根节点
void remove(int key){
spilt(root,key,x,z);
spilt(x,key-1,x,y);
y=merge(tr[y].l,tr[y].r);
root=merge(merge(x,y),z);
}
3.getrank(根据值找排名)
我们只需要按key-1切割树,那么排名就是x树的节点+1
非常简单
int getrank(int key){
spilt(root,key-1,x,y);
cout<<tr[x].size+1<<endl;
root=merge(x,y);
}
4.getnum(根据排名找值)
这个操作就复杂一些了,我们这次使用while循环求解
我们知道如果左子树的排名+1等于rank,那么我们就找到这个值了
如果不是,如果左子树排名大于等于rank,我们就去左子树找,否则就去右子树
本题当中一定能找到答案,因为题目说明
int getnum(int rank){
int p=root;
while(p){
if(tr[tr[p].l].size+1==rank)
break;
else if(tr[tr[p].l].size>=rank)
p=tr[p].l;
else{
rank-=tr[tr[p].l].size;
rank-=1;
p=tr[p].r;
}
}
cout<<tr[p].key<<endl;
}
5.寻找前驱(指的是比key小的最大值)
这只需要先按key-1切割,然后一直递归右节点就能找到答案
6.寻找后继
同上一直递归左节点就行
int pre(int key){
spilt(root,key-1,x,y);
int p=x;
while(tr[p].r){
p=tr[p].r;
}
cout<<tr[p].key<<endl;
root=merge(x,y);
}
int nxt(int key){
spilt(root,key,x,y);
int p=y;
while(tr[p].l){
p=tr[p].l;
}
cout<<tr[p].key<<endl;
root=merge(x,y);
}
--------------------------------------结束线
下面我给出洛谷p3369的AC代码,这也是一道模板题
我们学会了基础操作,但是还远远不够,继续加油,解决更难的题目!!!
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<map>
#include<vector>
using namespace std;
typedef long long ll;
const int N=5e5+10;
const int inf=0x3f3f3f3f;
int root,idx;
int x,y,z;
struct node{
int l,r;
int key;
int val;
int size;
}tr[N];
int get(int key){
tr[++idx].key=key;
tr[idx].size=1;
tr[idx].val=rand();
return idx;
}
void pushup(int u){
tr[u].size=tr[tr[u].l].size+tr[tr[u].r].size+1;
}
void spilt(int p,int key,int &x,int &y){ //按key分裂
if(!p) //分裂完毕赋空值
x=y=0;
else{
if(tr[p].key<=key){ //满足左子树条件,所有值小于key
x=p;
spilt(tr[p].r,key,tr[p].r,y);//递归右边节点
}
else{
y=p;
spilt(tr[p].l,key,x,tr[p].l);//同理,递归左边
}
pushup(p); //无论哪个值取到p,只需要更新p,那就更新完了节点
}
}
int merge(int x,int y){
if(!x||!y)
return x+y;//返回不为0的值
if(tr[x].val>=tr[y].val){ //根据二叉堆的性质
tr[x].r=merge(tr[x].r,y); //x中值都比y中的小
pushup(x);
return x; //父节点是x
}
else{
tr[y].l=merge(x,tr[y].l);
pushup(y);
return y;
}
}
int insert(int key){
spilt(root,key,x,y); //按key值分离
root=merge(merge(x,get(key)),y);//合并回来更新root
}
void remove(int key){
spilt(root,key,x,z);
spilt(x,key-1,x,y);
y=merge(tr[y].l,tr[y].r);
root=merge(merge(x,y),z);
}
int getrank(int key){
spilt(root,key-1,x,y);
cout<<tr[x].size+1<<endl;
root=merge(x,y);
}
int getnum(int rank){
int p=root;
while(p){
if(tr[tr[p].l].size+1==rank)
break;
else if(tr[tr[p].l].size>=rank)
p=tr[p].l;
else{
rank-=tr[tr[p].l].size;
rank-=1;
p=tr[p].r;
}
}
cout<<tr[p].key<<endl;
}
int pre(int key){
spilt(root,key-1,x,y);
int p=x;
while(tr[p].r){
p=tr[p].r;
}
cout<<tr[p].key<<endl;
root=merge(x,y);
}
int nxt(int key){
spilt(root,key,x,y);
int p=y;
while(tr[p].l){
p=tr[p].l;
}
cout<<tr[p].key<<endl;
root=merge(x,y);
}
int main(){
int t;
cin>>t;
while(t--){
int opt,x;
cin>>opt>>x;
switch(opt){
case 1:
insert(x);
break;
case 2:
remove(x);
break;
case 3:
getrank(x);
break;
case 4:
getnum(x);
break;
case 5:
pre(x);
break;
case 6:
nxt(x);
break;
}
}
}