平衡二叉树-splay c/c++代码实现

参考视频:https://www.bilibili.com/video/BV1wt411u7xL?t=1142讲的特别好!
注释都在代码中了

/*Keep on going Never give up*/
#pragma GCC optimize(3,"Ofast","inline")
#include <bits/stdc++.h>
const int maxn = 2e5+10;
const int MaxN = 0x3f3f3f3f;
const int MinN = 0xc0c0c00c;
typedef long long ll;
const int mod = 100000000;
using namespace std;

struct node{   //这里记录重复结点用的是cnt这个变量1代表这个数有一个,2代表这个数有两个。。。
    int l,r;
    int val,size,cnt;
}spl[maxn];
int cnt,root;

void new_node(int &now,int &val){  //新建结点
    now=++cnt;
    spl[now].val=val;
    spl[now].size++;
    spl[now].cnt++;
}
void update(int now){   //更新size
    spl[now].size=spl[spl[now].l].size+spl[spl[now].r].size+spl[now].cnt;
}
void zig(int &now){   //右旋
    int l=spl[now].l;
    spl[now].l=spl[l].r;
    spl[l].r=now;
    now=l;
    update(spl[now].r),update(now);
}
void zag(int &now){   //左旋
    int r=spl[now].r; //把他右子树的结点暂存一下,一会需要以右孩子为根节点
    spl[now].r=spl[r].l; //把根节点右子树的左子树挂在原来根节点的上右子树上
    spl[r].l=now; //把原来的根节点挂在原来的右子树(现在的根节点)上去
    now=r;   //现在的根节点更新
    update(spl[now].l),update(now); //检查树是否需要更新
}

void splaying(int x,int &y){   //
    if(x==y) return;   //如果到了我们指定的结点,return
    //主要是通过递归进行伸展操作,
    //通过从根节点不断递归到自己想要悬上去的结点
    //不过缺点就是常数可能比较大,但是好写一些
    int &l=spl[y].l,&r=spl[y].r;
    if(x==l) zig(y);
    else if(x==r) zag(y);
    else{
        if(spl[x].val<spl[y].val){
            if(spl[x].val<spl[l].val)
                splaying(x,spl[l].l),zig(y),zig(y);     //zigzig情况
            else splaying(x,spl[l].r),zag(l),zig(y);    //zagzig情况
        }
        else{
            if(spl[x].val>spl[r].val)
                splaying(x,spl[r].r),zag(y),zag(y);     //zagzag情况
            else splaying(x,spl[r].l),zig(r),zag(y);    //zigzag情况
        }
    }
}
void del_node(int now){   //找到删除的结点并将其删除
    splaying(now,root);  //先把要删除的结点延申到根节点
    //看看次数是否重复了,如果重复了结点直接-1即可(看内存池是如何定义的)
    if(spl[now].cnt>1){
        spl[now].size--;
        spl[now].cnt--;
    }
    else if(spl[root].r){  //找一下后继,建议看一下up的视频,讲的很清晰(17min开始看)
        int temp=spl[root].r;
        while (spl[temp].l) temp=spl[temp].l;
        splaying(temp,spl[root].r);
        spl[spl[root].r].l=spl[root].l;
        root=spl[root].r;
        update(root);
    }
    else root=spl[root].l;
}
void del(int &now,int val){   //找到那个删除的结点
    if(spl[now].val==val) del_node(now);
    else if(val<spl[now].val) del(spl[now].l,val);
    else del(spl[now].r,val);
}
void insert(int &now,int val){  //插入
    if(!now) new_node(now,val),splaying(now,root);  //新建结点并插入
    else if(val<spl[now].val) insert(spl[now].l,val);
    else if(val>spl[now].val) insert(spl[now].r,val);
    else spl[now].size++,spl[now].cnt++,splaying(now,root);
}
int get_rank(int val){
    int now=root,rank=1;
    //往树的右孩子走,同时减去加上他小的个数
    //(他的左子树的数都比他小,所以他的总排名肯定要比左边的所有数都高,
    //所以先用rank加上左数大小)
    while (now){
        if(spl[now].val==val){
            rank+=spl[spl[now].l].size;
            splaying(now,root);
            break;
        }
        if(val<=spl[now].val) now=spl[now].l;
        else{
            rank+=spl[spl[now].l].size+spl[now].cnt;
            now=spl[now].r;
        }
    }
    return rank;
}
int get_num(int rank){
    int now=root;
    while (now){
        int lsize=spl[spl[now].l].size;
        if(lsize+1<=rank&&rank<=lsize+spl[now].cnt){  //如果找到了直接break
            splaying(now,root);
            break;
        }
        else if(lsize>=rank) now=spl[now].l;
        else{
            //往树的右孩子走,同时减去比他小的个数
            //(他的左子树的数都比他小,所以直接减去左子树的大小)
            rank-=lsize+spl[now].cnt;
            now=spl[now].r;
        }
    }
    return spl[now].val;
}

int main()
{
    int n;
    cin>>n;
    for(int i=0;i<n;i++){
        int ch,x;
        scanf("%d%d",&ch,&x);
        if(ch==1) insert(root,x);
        if(ch==2) del(root,x);
        if(ch==3) printf("%d\n",get_rank(x));
        if(ch==4) printf("%d\n",get_num(x));
        if(ch==5) printf("%d\n",get_num(get_rank(x)-1));
        if(ch==6) printf("%d\n",get_num(get_rank(x+1)));
    }
    return 0;
}



猜你喜欢

转载自blog.csdn.net/xxxxxiao123/article/details/107188445