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 }