Splay,伸展树。之所以先写这个课内并不怎么常用的数据结构,是因为本人非常喜欢Splay,我觉得这是非常有美感且灵活的一种平衡树。在此先声明,我的伸展树写法来源于CLJ大牛,基础好的同学可以去他的博客中看看他的Splay实现模板,我的实现仅仅借鉴了CLJ大神的一点实现技巧而已。我的博文《心中的大牛博客列表》中有CLJ大神的博客链接。
还有很多同学可能并不了解Splay的思想,那么可以去看sqybi的文章《The magical splay》,百度文库就可以搜索到。这篇文章是我看过的里讲解splay最清晰的。里面的图和讲解都非常棒,里面的代码是Pascal的,但是没关系,只会C++的同学可以看我的代码。
在此先要讲解一下CLJ大神的这个splay实现技巧:充分利用C/C++中将bool型的true和false的值设置为1和0的特点,将树的每个节点的左右孩子指针用如下方式定义——node *son[2]。如此一来,son[0]便是左孩子,son[1]便是右孩子。此时,便可以在实现中,将左侧、右侧操作的代码写成一段,利用一个bool型变量来区分就好了。因此,代码基本会少一半!
我这么说大家肯定糊里糊涂的,不废话了,直接上代码,大家仔细看看代码就懂了。
题目:sjtuoj 1221。
清爽版:暂无。因为Splay的实现代码还是比较长的,因此并不考虑直接写,代码反而会很乱。
类实现版:
1 /* 2 * 题目:sjtuoj 1221 3 * oj链接:http://acm.sjtu.edu.cn/OnlineJudge 4 */ 5 #include <iostream> 6 #include <fstream> 7 #include <string> 8 #include <cstdio> 9 using namespace std; 10 11 const int minInt=1<<31; 12 const int maxInt=minInt-1; 13 14 struct dot 15 { 16 int c,num,size; 17 dot *son[2],*up; // son[false]是左孩子,son[true]是右孩子,up是父亲 18 19 dot(int value=0) { c=value; num=size=1; son[false]=son[true]=up=0; } 20 bool get(dot *lr) { return son[true]==lr; } 21 void add_up(int n) { for(dot *u=this;u;u=u->up) u->size+=n; } 22 dot* born(bool k,dot* lr) 23 { 24 son[k]=lr; 25 if(lr) { lr->up=this; add_up(lr->size); } 26 return this; 27 } 28 dot* kill(bool k) 29 { 30 dot *lr=son[k]; son[k]=0; 31 if(lr) { lr->up=0; add_up(-lr->size); } 32 return lr; 33 } 34 }; 35 36 int vv(dot *u) { return u?u->c:0; } 37 int nn(dot *u) { return u?u->num:0; } 38 int ss(dot *u) { return u?u->size:0; } 39 40 class Splay 41 { 42 public: 43 dot *Root,*Min,*Max; 44 private: 45 void zg(dot *x) 46 { 47 dot *y=x->up,*z=y->up; 48 bool i=(z?z->get(y):0),k=y->get(x); // 此处i==0时,i无意义 49 50 if(z) z->kill(i); 51 x->born(!k,y->born(k,y->kill(k)->kill(!k))); 52 if(z) z->born(i,x); 53 54 if(y==Root) Root=x; // 维护Root,可能也是保险?no!This is useful! 55 } 56 void splay(dot *x,dot *up=0) // 将x旋转到其父亲为up的位置 57 { 58 dot *y,*z; 59 while(x->up!=up) // x还没到指定位置 60 { 61 y=x->up; if(y->up==up) { zg(x); break; } // 如果y是指定位置,则将x旋转到y 62 z=y->up; zg((z->get(y)==y->get(x))?y:x); zg(x); // 将x旋转到z即可 63 } 64 } 65 void recycle(dot *p) 66 { 67 if(!p) return; 68 recycle(p->son[false]); recycle(p->son[true]); 69 delete p; 70 } 71 dot* next(dot *p,bool k) 72 { 73 splay(p); 74 75 dot *u=p->son[k]; 76 while(u->son[!k]) u=u->son[!k]; 77 return u; 78 } 79 public: 80 Splay() 81 { 82 Min=Root=new dot(minInt); Max=new dot(maxInt); 83 Min->born(true,Max); 84 } 85 int size() { return Root->size-2; } 86 dot* Find(int c) 87 { 88 dot *u=Root; 89 while(u&&u->c!=c) u=u->son[c>u->c]; 90 return u; 91 } 92 93 void Insert(int c) 94 { 95 bool k; 96 dot *u=0,*v=Root; 97 98 while(v&&v->c!=c) 99 { 100 u=v; 101 k=(c>v->c); 102 v=v->son[k]; 103 } 104 if(v) { ++v->num; v->add_up(1); } else splay(u->born(k,new dot(c))->son[k]); 105 } 106 void Delete(int c) 107 { 108 dot *p=Find(c),*l,*r; 109 110 --p->num; p->add_up(-1); 111 if(p->num==0) 112 { 113 l=next(p,false); r=next(p,true); 114 splay(l); splay(r,Root); 115 recycle(r->kill(false)); 116 } 117 } 118 void Delete(int cl,int cr) 119 { 120 dot *L,*R,*l,*r; 121 122 Insert(cl); Insert(cr); 123 L=Find(cl); R=Find(cr); 124 125 l=next(L,false); r=next(R,true); 126 splay(l); splay(r,Root); 127 recycle(r->kill(false)); 128 } 129 int Find_ith(int i,dot *u) // 这里的三种情况,用到了其先后顺序,故顺序不能轻易改变 130 { 131 int size=ss(u->son[false]),mid=u->num; 132 if(i<=size) return Find_ith(i,u->son[false]); 133 if(i<=size+mid) return u->c; 134 return Find_ith(i-size-mid,u->son[true]); 135 } 136 }; 137 138 Splay A; // 创建一棵Splay树,名叫A 139 140 int main() 141 { 142 // 本题,假设题目数据均在 minInt+1 ~ maxInt-1 范围内 143 int n,x,y; cin>>n; 144 string order; 145 for(int i=1;i<=n;++i) 146 { 147 cin>>order; 148 if(order=="insert") { cin>>x; A.Insert(x); } 149 if(order=="delete") { cin>>x; A.Delete(x); } 150 if(order=="delete_less_than") { cin>>y; A.Delete(minInt+1,y-1); } 151 if(order=="delete_greater_than") { cin>>x; A.Delete(x+1,maxInt-1); } 152 if(order=="delete_interval") { cin>>x>>y; A.Delete(x+1,y-1); } 153 if(order=="find") { cin>>x; cout<<(A.Find(x)?"Y":"N")<<endl; } 154 if(order=="find_ith") 155 { 156 cin>>x; 157 if(A.size()<x) { cout<<"N"<<endl; continue; } 158 cout<<A.Find_ith(x+1,A.Root)<<endl; // 有一个Min,所以得找第x+1个 159 } 160 } 161 162 return 0; 163 }