Splay算法

转载自:http://blog.csdn.net/ric_shooter/article/details/20636487


这是我的第一篇博文,由于被splay坑得太惨,所以毅然决定以此开博。

        蜘蛛快来:伸展树
解释splay的文章满大街都是,但用pascal的毕竟少,所以这是用pascal代码来解释的(C++代码在最后)
        知道BST的请自动跳到第6段
        要学splay,首先要知道BST(二叉排序树)的概念
 它或是一棵空树;或者是具有下列性质的二叉树:
1)若左子树不空,则左子树上所有结点的值均小于它的根结点的值;
(2)若右子树不空,则右子树上所有结点的值均大于它的根结点的值;
(3)左、右子树也分别为二叉排序树;

BST可以简单地用递归实现,下面是插入节点的操作:
[delphi]  view plain  copy
  1. procedure ins(p,k:longint);  
  2. begin  
  3.  if p<a[k] then  
  4.     if next[k].l=0 then  
  5.       begin  
  6.         inc(tot);  
  7.         next[k].l:=tot;  
  8.         a[tot]:=p;  
  9.       end else  
  10.         ins(p,next[k].l)else  
  11.         if next[k].r=0 then  
  12.         begin  
  13.           inc(tot);  
  14.           next[k].r:=tot;  
  15.           a[tot]:=p;  
  16.        end else  
  17.          ins(p,next[k].r);  
  18. end;  




        不难看出,裸的BST很容易被卡,虽然期望复杂度是O(log n),但对付退化成一条链的数据就变成O(n).
        所以,平衡树(BBST)应运而生
BBST有treap、splay、AVL、RBT、SBT等等
        这里只讲splay。伸展树不像AVL,它不保证严格的平衡,但是编程复杂度大大降低,效率有点似乎萎。但功能很强大,几乎能实现其他平衡树的一切功能,是性价比很高的东西。
基本概念1:旋转
①ZIG与ZAG

       若B是根节点左节点,则对其ZIG,把B拎起来,拉到根的位置,让A变成B的孩子,发现B有3个孩子了,就把E作为A的左儿子

      显然,这样不会违反BST的性质,ZAG就是ZIG的反演。 
②Zig-Zig与Zag-Zag
          若节点x的父节点y不是根节点,且x与y同时是各自父节点的左孩子,则进行ZIG-ZIG
          若都是右孩子,则进行ZAG-ZAG
          Zig-Zig:先Zig【y】节点,再Zig【x】节点


         Zag-Zag:先Zag【y】节点,再Zag【x】节点
        建议读者自己画一画,理解一下。
③Zig-Zag 与Zag-Zig
        若节点x的父节点y不是根节点,x与y中一个是其父节点的左孩子而另一个是其父节点的右孩子,则进行此操作。
        这里的Zig和Zag与Zig-Zig及Zag-Zag里的不同,这里的旋转都是对【x】节点进行的。

       有人读到这里可能会问,为什么要定义先旋转y,再旋转x的双旋呢?,都只对x进行旋转操作不是更好么?而且都只对x旋转对其他节点相对高度改变小,不正符合了splay把常访问节点提上来的初衷么?我也有这样的疑问,但是经过无数数据的测试,定义如是双旋比只对x旋转优了50%,这个Tarjan会证,我不会……
基本概念②:伸展(splay)
        这个概念容易理解,就是对每次被查找、插入等操作的节点用上述方法旋转到根的位置。
代码:定义
father数组——》存储该节点的父节点
son数组——》son[x,1]表示x节点的左儿子,son[x,2]表示x节点的右儿子
Data数组——》存储节点的值
value数组——》存储该节点的值出现了几次
count数组——》count[x]表示以x为根的子树结点数量
其实用记录类型写会更漂亮和方便,但是这样存储有些地方可以压缩代码,虽然大多数人不喜欢,调试也麻烦,然而为了培养读者自己写代码的能力……(好吧其实是我不想改了)
Code:旋转操作
[delphi]  view plain  copy
  1. procedure Rotate(x,w:longint);inline;//x是要旋转的节点,w=1左旋,w=2右旋  
  2. var y:longint;  
  3. begin  
  4.  y:=father[x];  
  5.  count[y]:=count[y]-count[x]+count[son[x,w]];  
  6.  count[x]:=count[x]-count[son[x,w]]+count[y];  
  7.  son[y,3-w]:=son[x,w];//若右旋,将其父节点的左儿子设置为当前节点的右儿子,相反就……  
  8.  if son[x,w]<>0 then father[son[x,w]]:=y;//设置当前节点儿子的父节点,相反也是……  
  9.  father[x]:=father[y];  
  10.  if father[y]<>0 then  
  11.    if y=son[father[y],1then son[father[y],1]:=x else son[father[y],2]:=x;  
  12.    //修改x与其旋转后父节点的关联  
  13.  father[y]:=x;son[x,w]:=y;//设置旋转后x与y的关系  
  14. end;  



伸展操作
[delphi]  view plain  copy
  1. procedure splay(x:longint);inline;//伸展操作无需多解释,细心即可  
  2. var y:longint;  
  3. begin  
  4.  while father[x]<>0 do  
  5.    begin  
  6.      y:=father[x];  
  7.      if father[y]=0 then  
  8.          if x=son[y,1]then rotate(x,2)//ZIG  
  9.                       else rotate(x,1)//ZAG  
  10.        else  
  11.          if y=son[father[y],1then  
  12.            if x=son[y,1]  
  13.              then begin rotate(y,2);rotate(x,2)end//ZIG-ZIG  
  14.              else begin rotate(x,1);rotate(x,2)end//ZAG-ZIG  
  15.          else  
  16.            if x=son[y,2]  
  17.              then begin rotate(y,1);rotate(x,1)end//ZAG-ZAG  
  18.              else begin rotate(x,2);rotate(x,1)end//ZIG-ZAG  
  19.    end;  
  20.    root:=x;//x成为根  
  21. end;  



查找
[delphi]  view plain  copy
  1. function search(x,w:longint):longint;inline;//在以x为根的子树中,w为要查询的数,返回节点编号  
  2. begin  
  3.  while data[x]<>w do  
  4.    begin  
  5.          if w=data[x] then exit(x);//找到就退出  
  6.      if w<data[x] then//这里操作与BST一样  
  7.        begin  
  8.          if son[x,1]=0 then break;  
  9.          x:=son[x,1];  
  10.        end else  
  11.        begin  
  12.          if son[x,2]=0 then break;  
  13.          x:=son[x,2];  
  14.        end  
  15.    end;  
  16.  exit(x);  
  17. end;  



插入
[delphi]  view plain  copy
  1. procedure insert(w:longint);inline;  
  2. var k,kk:longint;flag:boolean;  
  3. begin  
  4.  if tot=0 then//tot记录当前树中的节点总数  
  5.    begin  
  6.      inc(tot);  
  7.      father[1]:=0;count[1]:=1;data[1]:=w;root:=1;value[1]:=1;//root是根的编号  
  8.      exit;  
  9.    end;  
  10.  k:=search(root,w);  
  11.  if data[k]=w then//如果该数值已存在于树中,就只要……  
  12.      begin  
  13.       inc(value[k]);kk:=k;  
  14.       flag:=true;  
  15.      end else  
  16.       begin//否则新建节点  
  17.         inc(tot);  
  18.         data[tot]:=w;father[tot]:=k;count[tot]:=1;value[tot]:=1;  
  19.         if data[k]>w then son[k,1]:=tot else son[k,2]:=tot;  
  20.         flag:=false;  
  21.       end;  
  22.  while k<>0 do  
  23.  begin  
  24.    inc(count[k]);//更新count值,自己yy一下  
  25.    k:=father[k];  
  26.  end;  
  27.  if flag then splay(kk)else splay(tot);//flag决定伸展哪个节点  
  28. end;  


求极值(类似于查找,自己yy即可)
[delphi]  view plain  copy
  1. function Extreme(x,w:longint):longint;inline;//x是要访问子树的根,w=1求max,w=2求min  
  2. const lala:array[1..2]of longint=(maxlongint,-maxlongint);  
  3. var k:longint;  
  4. begin  
  5.  k:=search(x,lala[w]);  
  6.  Extreme:=data[k];  
  7.  splay(k);  
  8. end;  


删除(核心思想即伸展欲删节点,合并左右子树)
[delphi]  view plain  copy
  1. procedure delete(x:longint);inline;//x是要删除的【数值】  
  2. var k,y:longint;  
  3. begin  
  4.  k:=search(root,x);  
  5.  if data[k]<>x then splay(k)//如果此数不在树中,伸展k  
  6.   
  7.  else  
  8.  begin  
  9.    splay(k);  
  10.   
  11.    if value[k]>1 then begin dec(value[k]);dec(count[k]);end else  
  12.    if son[k,1]=0 then  
  13.        begin  
  14.          y:=son[k,2];  
  15.                  son[k,2]:=0;count[k]:=0;data[k]:=0;value[k]:=0;  
  16.                  root:=y;father[root]:=0;  
  17.        end else  
  18.        begin  
  19.          father[son[k,1]]:=0;//切断左子树与根的关联  
  20.          y:=Extreme(son[k,1],1);//左子树中的max  
  21.          son[root,2]:=son[k,2];//左右子树合并  
  22.          count[root]:=count[root]+count[son[k,2]];  
  23.          if son[root,2]<>0 then father[son[root,2]]:=root;  
  24.                  data[k]:=0;son[k,1]:=0;son[k,2]:=0;value[k]:=0;  
  25.        end  
  26.  end  
  27. end;//有些赋为0的操作其实可以省略  



求前驱后继
[delphi]  view plain  copy
  1. function pred(x:longint):longint;inline;//求前驱  
  2. var k:longint;  
  3. begin  
  4.  k:=search(root,x);splay(k);  
  5.  if data[k]<x then exit(data[k]);  
  6.  exit(Extreme(son[k,1],1));  
  7. end;  
  8. function succ(x:longint):longint;inline;//求后继  
  9. var k:longint;  
  10. begin  
  11.  k:=search(root,x);splay(k);  
  12.  if data[k]>x then exit(data[k]);  
  13.  exit(Extreme(son[k,2],2));  
  14. end;  



求第k极值
[delphi]  view plain  copy
  1. function kth(x,w:longint):longint;inline;//w=1为求第x小值,w=2为求第x大值  
  2. var i:longint;  
  3. begin  
  4.  i:=root;  
  5.  while not((x>=count[son[i,w]]+1)and(x<=count[son[i,w]]+value[i]))and (i<>0)do  
  6.    begin  
  7.      if x>count[son[i,w]]+value[i] then  
  8.        begin  
  9.          x:=x-count[son[i,w]]-value[i];  
  10.          i:=son[i,3-w];  
  11.        end  
  12.        else i:=son[i,w];  
  13.    end;  
  14.    kth:=i;  
  15.    splay(i);  
  16. end;  



求x是第几大
[delphi]  view plain  copy
  1. function findnum(x:longint):longint;inline;  
  2. var K:longint;  
  3. begin  
  4.  k:=search(root,x);splay(k);  
  5.  root:=k;  
  6.  exit(count[son[k,1]]+1);  
  7. end;  



我能想到的基本操作大概就这些,最后推荐一道模板题
这题的code就是把上面的过程函数拼起来就行。
然后下面是C++的完整代码
[cpp]  view plain  copy
  1. #include<cstdio>  
  2. #include<iostream>  
  3. #include <cstdlib>  
  4.   
  5. using namespace std;  
  6.   
  7. int n,root,i,tot,opt,x;  
  8. int father[100000],count[100000],data[100000],value[100000];  
  9. int son[100000][3];  
  10.   
  11. inline void Rotate(int x,int w)  
  12. {  
  13.     int y;  
  14.     y=father[x];  
  15.     count[y]=count[y]-count[x]+count[son[x][w]];  
  16.     count[x]=count[x]-count[son[x][w]]+count[y];  
  17.     son[y][3-w]=son[x][w];  
  18.     if (son[x][w]) father[son[x][w]]=y;  
  19.     father[x]=father[y];  
  20.     if (father[y])  
  21.         if (y==son[father[y]][1]) son[father[y]][1]=x;  
  22.             else son[father[y]][2]=x;  
  23.     father[y]=x;  
  24.     son[x][w]=y;  
  25. }  
  26.   
  27. inline void splay(int x)  
  28. {  
  29.     int y;  
  30.     while (father[x])  
  31.     {  
  32.         y=father[x];  
  33.         if (!father[y])  
  34.             if (x==son[y][1]) Rotate(x,2);  
  35.             else Rotate(x,1);  
  36.         else  
  37.             if (y==son[father[y]][1])  
  38.                 if (x==son[y][1])  
  39.                 {  
  40.                     Rotate(y,2);Rotate(x,2);  
  41.                 }  
  42.                 else  
  43.                 {  
  44.                     Rotate(x,1);Rotate(x,2);  
  45.                 }  
  46.                 else  
  47.                 if (x==son[y][2])  
  48.                 {  
  49.                     Rotate(y,1);Rotate(x,1);  
  50.                 }  
  51.                 else  
  52.                 {  
  53.                     Rotate(x,2);Rotate(x,1);  
  54.                 }  
  55.     }  
  56.     root=x;  
  57. }  
  58.   
  59. inline int search(int x,int w)  
  60. {  
  61.     while (data[x]!=w)  
  62.     {  
  63.         if (w==data[x]) return x;  
  64.         if (w<data[x])  
  65.         {  
  66.             if (!son[x][1]) break;  
  67.             x=son[x][1];  
  68.         }  
  69.         else  
  70.         {  
  71.             if (son[x][2]==0) break;  
  72.             x=son[x][2];  
  73.         }  
  74.     }  
  75.     return x;  
  76. }  
  77.   
  78. inline void insert(int w)  
  79. {  
  80.     int k,kk;bool flag;  
  81.     if (!tot)  
  82.     {  
  83.         tot=1;  
  84.         father[1]=0;count[1]=1;data[1]=w;root=1;value[1]=1;  
  85.         return;  
  86.     }  
  87.     k=search(root,w);  
  88.     if (data[k]==w)  
  89.     {  
  90.         value[k]++;kk=k;  
  91.         flag=true;  
  92.     }  
  93.     else  
  94.     {  
  95.         tot++;  
  96.         data[tot]=w;  
  97.         father[tot]=k;  
  98.         count[tot]=1;  
  99.         value[tot]=1;  
  100.         if (data[k]>w) son[k][1]=tot;else son[k][2]=tot;  
  101.         flag=0;  
  102.     }  
  103.     while (k)  
  104.     {  
  105.         count[k]++;  
  106.         k=father[k];  
  107.     }  
  108.     if (flag) splay(kk);else splay(tot);  
  109. }  
  110.   
  111. inline int Extreme(int x,int w)  
  112. {  
  113.     const int lala[3]={0,2147483647,-2147483647};  
  114.     int k,tmp;  
  115.     k=search(x,lala[w]);  
  116.     tmp=data[k];  
  117.     splay(k);  
  118.     return tmp;  
  119. }  
  120.   
  121. inline void del(int x)  
  122. {  
  123.     int k,y;  
  124.     k=search(root,x);  
  125.     if (data[k]!=x) splay(k);else  
  126.     {  
  127.         splay(k);  
  128.         if (value[k]>1)  
  129.         {  
  130.             value[k]--;  
  131.             count[k]--;  
  132.         }  
  133.         else  
  134.         if (!son[k][1])  
  135.         {  
  136.             y=son[k][2];  
  137.             son[k][2]=0;  
  138.             count[k]=0;  
  139.             data[k]=0;  
  140.             value[k]=0;  
  141.             root=y;  
  142.             father[root]=0;  
  143.         }  
  144.         else  
  145.         {  
  146.             father[son[k][1]]=0;  
  147.             y=Extreme(son[k][1],1);  
  148.             son[root][2]=son[k][2];  
  149.             count[root]=count[root]+count[son[k][2]];  
  150.             if (son[root][2]!=0) father[son[root][2]]=root;  
  151.                 data[k]=0;son[k][1];son[k][2]=0;  
  152.                 value[k]=0;  
  153.         }  
  154.     }  
  155. }  
  156.   
  157. inline int pred(int x)  
  158. {  
  159.     int k;  
  160.     k=search(root,x);  
  161.     splay(k);  
  162.     if (data[k]<x) return data[k];  
  163.     return Extreme(son[k][1],1);  
  164. }  
  165.   
  166. inline int succ(int x)  
  167. {  
  168.     int k;  
  169.     k=search(root,x);  
  170.     splay(k);  
  171.     if (data[k]>x) return data[k];  
  172.     return Extreme(son[k][2],2);  
  173.   
  174. }  
  175.   
  176. inline int kth(int x,int w)  
  177. {  
  178.     int i,tmp;  
  179.     i=root;  
  180.     while (!((x>=count[son[i][w]]+1)&&(x<=count[son[i][w]]+value[i]))&&(i!=0))  
  181.     {  
  182.         if (x>count[son[i][w]]+value[i])  
  183.         {  
  184.             x=x-count[son[i][w]]-value[i];  
  185.             i=son[i][3-w];  
  186.         }  
  187.         else  
  188.         i=son[i][w];  
  189.     }  
  190.     tmp=i;  
  191.     splay(i);  
  192.     return tmp;  
  193. }  
  194.   
  195. inline int findnum(int x)  
  196. {  
  197.     int k;  
  198.     k=search(root,x);splay(k);  
  199.     root=k;  
  200.     return count[son[k][1]]+1;  
  201. }  
  202.   
  203. int main()  
  204. {  
  205.     scanf("%d",&n);  
  206.     for(i=1;i<=n;i++)  
  207.     {  
  208.         if (i==3)  
  209.         i=3;  
  210.         scanf("%d%d",&opt,&x);  
  211.         switch(opt)  
  212.         {  
  213.             case 1:insert(x);break;  
  214.             case 2:del(x);break;  
  215.             case 3:printf("%d\n",findnum(x));break;  
  216.             case 4:printf("%d\n",data[kth(x,1)]);break;  
  217.             case 5:printf("%d\n",pred(x));break;  
  218.             case 6:printf("%d\n",succ(x));break;  
  219.             default:break;  
  220.         }  
  221.     }  
  222.     return 0;  
  223. }  


~~~~~~~~~~~~~~感谢阅读~~~~~~~~~~~~~~
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值