Splay 指针&&无父节点

    其实,代码还是99%的和 Running-coder 的相似,但是,没有但是……

    Splay在 NOIP && NOI 有一定地位,学一学,总比打暴搜强……

    Splay的功能有很多,百度一下,你就知道……

    Splay 中文名称 伸展树,是一种很666的平衡树,而平衡树就是优化的二叉搜索树,所以在二叉搜索树的基础上学习Splay会更好,然而我就直接跳过了二叉搜索树……

    

    代码:

    

  1 #include<cstdio>
  2 #include<iostream>
  3 #include<cstring>
  4 #include<string>
  5 #include<cmath>
  6 #include<algorithm>
  7 #include<ctime>
  8 #include<cstdlib>
  9 #include<cfloat>
 10 
 11 using namespace std;
 12 
 13 int MAX_INT=214746000;
 14 struct ez{
 15     int key;                         //key--当前节点的值
 16     int num;                         //num--当前节点值的数目
 17     int size;                        //size--当前节点的子节点个数
 18     ez *ch[2];                       //ch[0]--左孩子,ch[1]--右孩子。
 19     
 20     int cmp(int x){                  //比较函数,比较当前节点的值和要查询的值的大小
 21         if(x==key) return -1;                          
 22         else return x>key;
 23     }
 24     void maintain(){                 //maintain函数,用于计算每个节点的
 25         size=num;
 26         if(ch[0]!=NULL) size+=ch[0]->size;
 27         if(ch[1]!=NULL) size+=ch[1]->size;  //节点不为空,
 28     }
 29 };
 30 ez *root=NULL;                              //开始,根节点为空
 31 int f,x,n,u;
 32 
 33 void rotate(ez* &p,bool f){                 //旋转函数
 34     ez *t=p->ch[f^1];                                  
 35     if(p->ch[f^1]!=NULL)
 36     p->ch[f^1]=t->ch[f];
 37     t->ch[f]=p;                             //旋转过程,并不好描述,但只要背过代码,就完全没问题
 38     
 39     p->maintain();
 40     t->maintain();                          //处理size
 41     p=t;
 42 }
 43 
 44 void insert(ez* &p,int x){                   //插入函数
 45     if(p==NULL){                             //如果p指针为空指针,就新建一个指针
 46         p=(ez *)malloc(sizeof(ez));
 47         p->key=x;
 48         p->num=1;
 49         p->size=1;
 50         p->ch[0]=NULL;
 51         p->ch[1]=NULL;
 52         return;
 53     }else
 54     if(x==p->key){   //否则,如果当前指针的值与要插入的值相等,就将当前指针的num和size都加一
 55         p->size++;
 56         p->num++;
 57         return;
 58     }
 59     else
 60     if(x>p->key){  //否则继续寻找:若要查找的值比当前节点的值大就向右儿子查找,同时当前节点的子节点数目++
 61         insert(p->ch[1],x);
 62         p->size++;
 63         return;
 64     }
 65     else
 66     if(x<p->key){         //如果小,就向左儿子查找,当前子节点数目++
 67         insert(p->ch[0],x);
 68         p->size++;
 69         return;
 70     }
 71 }
 72 
 73 void del(ez* &p,int x){            //删除函数
 74     if(p==NULL) return;            //删除空节点,会 RE爆0,身败名裂!!!!
 75     if(p->key==x)                  //如果当前节点的值和要删除的值相同
 76     if(p->num>1){                  //如果该数的数目大于1,就num和size--就ok了。
 77         p->num--;
 78         p->size--;
 79         return;
 80     }
 81     else{                 //否则,把该节点旋转到一个叶子节点,这样就没有后顾之忧了,爆掉他!
 82         ez *t=p;
 83         if(p->ch[0]==NULL){
 84             p=p->ch[1];
 85             free(t);
 86             return;
 87         }else
 88         if(p->ch[1]==NULL){
 89             p=p->ch[0];
 90             free(t);
 91             return;
 92         }else{
 93             int o=rand()%1;
 94             rotate(p,o);
 95             del(p->ch[o],x);
 96             p->size--;
 97         }
 98         
 99     } 
100     else{                   //寻找这个节点
101         if(x<p->key) del(p->ch[0],x);
102         else del(p->ch[1],x);
103         p->size--;
104     }
105 }
106 
107 void splay(ez* &p,int x){ //没有这一部分的Splay就是一棵普通的二叉搜索树,拥有将某一点强行抽到根节点的能力
108     int d1=p->cmp(x);                                
109     if(d1==-1||p->ch[d1]==NULL) return;  //判断能否继续旋转
110     int d2=p->ch[d1]->cmp(x);
111     if(d2==-1||p->ch[d1]->ch[d2]==NULL){   //如果这能旋转一次,就旋转一次,否则 双旋
112         rotate(p,d1^1);
113         return;
114     }else{
115     splay(p->ch[d1]->ch[d2],x);           //判断能否在d1的基础上继续旋转
116     if(d1==d2){
117         rotate(p,d1^1);
118         rotate(p,d2^1);
119         return;
120     }else{
121         rotate(p->ch[d1],d2^1);
122         rotate(p,d1^1);
123         return;             //注意:不同的结果,旋转的方式不一样,如果实在理解不了,就背过代码就行了
124     }
125     }
126 }
127 
128 int pre(int x){ //查前驱,方法:把要查的节点提到根节点,那么从他的左子树一直向右查询,得到的最终结果就是x的前驱,
129     splay(root,x);
130     if(root->key<x) return root->key;
131     else{
132         if(root->ch[0]==NULL) return -MAX_INT;
133         else{
134             splay(root->ch[0],MAX_INT);
135             return root->ch[0]->key;
136         }
137     } 
138 } 
139 
140 int succ(int x){               //查后继,方法恰好是求前驱反过来
141     splay(root,x);
142     if(root->key>x) return root->key;
143     else{
144         if(root->ch[1]==NULL) return -MAX_INT;
145         else{
146             splay(root->ch[1],-MAX_INT);
147             return root->ch[1]->key;
148         }
149     }
150 }
151 
152 int kth(ez* &p,int x){ //求第k小的数
153     int s=0;  //方法:从根节点开始搜索,如果x比当前节点小就向左找,否则,名次上减去左子树的节点数,向右找。
154     if(p->ch[0]!=NULL) s=p->ch[0]->size;
155     if(x<=s) return kth(p->ch[0],x); else
156     if(x<=s+p->num) return p->key;else
157     return kth(p->ch[1],x-s-p->num);
158 }
159 
160 int rank(int x){ //求当前点的排名
161     splay(root,x);   //方法:从根节点搜索,如果在左边,就向左走,否则加上左边的节点数再向右找
162     if(root->ch[0]==NULL) return 1;
163     else return root->ch[0]->size+1;
164 }
165 
166 int main(){
167     srand(time(0)+20010930);
168     
169     scanf("%d",&n);
170     for(int i=1;i<=n;i++){
171         scanf("%d",&f);
172         switch(f){
173             case 1:{
174                 scanf("%d",&x);
175                 insert(root,x);
176                 break;
177             }
178             case 2:{
179                 scanf("%d",&x);
180                 del(root,x);
181                 splay(root,x);
182                 break;
183             }
184             case 3:{
185                 scanf("%d",&x);
186                 printf("%d\n",rank(x));
187                 splay(root,x);
188                 break;
189             }
190             case 4:{
191                 scanf("%d",&x);
192                 printf("%d\n",kth(root,x));
193                 splay(root,x);
194                 break;
195             }
196             case 5:{
197                 scanf("%d",&x);
198                 printf("%d\n",pre(x));
199                 splay(root,x);
200                 break;
201             }
202             case 6:{
203                 scanf("%d",&x);
204                 printf("%d\n",succ(x));
205                 splay(root,x);
206                 break;
207             }
208         }
209     }
210 return 0; 211 }

 

转载于:https://www.cnblogs.com/Misaki-Mei/p/7347957.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值