AcWing 算法提高课 treap平衡树

1、基本性质

tree+heap=treap

平衡树包含treap 红黑树 splay sbt AVL等等

splay比较常用

treap=

①BST 二叉搜索树

+

②heap

2、set不能做的操作

 

 ⑤和⑥这种与排名相关的操作比较困难

3、treap的实现

思想:让二叉搜索树尽量变得随机(以大根堆为例)

 

key是搜索树的值,val是大根堆的值

使一棵树同时满足是二叉搜索树,同时还要满足是大根堆

注意:平衡树的初始状态一般会加两个哨兵,正无穷和负无穷,防止边界问题

 

左旋和右旋操作:为了交换节点

性质:旋转后,不会影响中序遍历的顺序。

 

插入操作:按二叉搜索树的性质插入点后,给val赋值,并且如果val不满足大根堆,则用旋转操作转上去(同时还可以保证平衡树)

 

删除操作:旋转到叶节点,然后删除。注意还要维护堆的性质,故哪个子树的val更大就和哪边旋转。

模板(例题):https://www.acwing.com/problem/content/255/

复制代码
#include<bits/stdc++.h>

#define fore(x,y,z) for(LL x=(y);x<=(z);x++)
#define forn(x,y,z) for(LL x=(y);x<(z);x++)
#define rofe(x,y,z) for(LL x=(y);x>=(z);x--)
#define rofn(x,y,z) for(LL x=(y);x>(z);x--)
#define pub push_back
#define all(x) (x).begin(),(x).end()
#define fi first
#define se second

using namespace std;
typedef long long LL;
typedef pair<int,int> PII;
typedef pair<LL,LL> PLL;
const int N=100010;
int INF=0x3f3f3f3f;
int n;
struct Node
{
    int l,r;
    int key,val;
    int cnt,sz;
}tr[N];
int root,idx;

int GetNode(int key)//创建节点
{
    tr[++idx].key=key;
    tr[idx].val=rand();
    tr[idx].cnt=tr[idx].sz=1;
    return idx;
}
void PushUp(int p)
{
    tr[p].sz=tr[tr[p].l].sz+tr[tr[p].r].sz+tr[p].cnt;
    
}
void Build()
{
    GetNode(-INF);
    GetNode(INF);
    root=1;
    tr[1].r=2;
    PushUp(root);
}
void zig(int &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 Insert(int &p,int key)
//由于需要满足堆的性质,故需要在函数里对p进行旋转,故传引用
{
    if(!p) p=GetNode(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)//不是叶子节点
        {
            //根据左右的存在性以及val的大小判断左旋还是右旋
            if(tr[p].r==0||tr[tr[p].l].val>tr[tr[p].r].val)
            {
                zig(p);
                Remove(tr[p].r,key);
            }
            else
            {
                zag(p);
                Remove(tr[p].l,key);
            }
        }
        else
        {
            p=0;//叶子节点直接删除
        }
    }
    else
    {
        if(tr[p].key>key)
        {
            Remove(tr[p].l,key);
        }
        else
        {
            Remove(tr[p].r,key);
        }
    }
    PushUp(p);
}
int GetRank(int p,int key)//返回在p为根的子树中的排名
{
    if(!p) return 0;
    if(tr[p].key==key)
    {
        return tr[tr[p].l].sz+1;
    }
    else if(tr[p].key>key)
    {
        return GetRank(tr[p].l,key);
    }
    else
    {
        return GetRank(tr[p].r,key)+tr[p].cnt+tr[tr[p].l].sz;
    }
}
int GetKey(int p,int rank)//返回在p为根的子树中排名为rank的key
{
    if(!p) return INF;
    if(tr[tr[p].l].sz>=rank) return GetKey(tr[p].l,rank);
    if(tr[tr[p].l].sz>=rank) return GetKey(tr[p].l,rank);
    if(tr[tr[p].l].sz+tr[p].cnt>=rank) return tr[p].key;
    else return GetKey(tr[p].r,rank-(tr[tr[p].l].sz+tr[p].cnt));
}
int GetPrev(int p,int key)//严格小于key的最大数
{
    if(!p) return -INF;
    if(tr[p].key>=key)
    {
        return GetPrev(tr[p].l,key);
    }
    else
    {
        return max(tr[p].key,GetPrev(tr[p].r,key));
        //max的原因是,GetPrev没找到返回-INF,找到了则比tr[u].key大
    }
}
int GetNext(int p,int key)
{
    if(!p) return INF;
    if(tr[p].key<=key)
    {
        return GetNext(tr[p].r,key);
    }
    else
    {
        return min(tr[p].key,GetNext(tr[p].l,key));
        //与GetPrev对称
    }
}
void YD()
{
    Build();
    cin>>n;
    while(n--)
    {
        int op;int x;
        cin>>op>>x;
        if(op==1)
        {
            Insert(root,x);
        }
        if(op==2)
        {
            Remove(root,x);
        }
        if(op==3)
        {
            cout<<GetRank(root,x)-1<<endl;
        }
        if(op==4)
        {
            cout<<GetKey(root,x+1)<<endl;
        }
        if(op==5)
        {
            cout<<GetPrev(root,x)<<endl;
        }
        if(op==6)
        {
            cout<<GetNext(root,x)<<endl;
        }
    }
}
 
int main()
{
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    int T=1;
    //cin >> T;
    while (T--)
    {
        YD();
    }
    return 0;
}
View Code
复制代码

 

posted @   80k  阅读(31)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
点击右上角即可分享
微信分享提示