平衡二叉树-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; }
数据结构算法学习 文章被收录于专栏
算法学习记录