zoukankan      html  css  js  c++  java
  • Splay

    注:本文改自http://blog.csdn.net/changtao381/article/details/8936765与百度百科伸展树。

    (代码为本人原创,有两百多行,第一次很难写,但是写多了就好了。)

    博客原作者若有问题可联系本人(815344231@qq.com),多谢。

    类别:二叉排序树

    空间效率:O(n)
    时间效率:O(log n)内完成插入、查找、删除操作
    创造者:Daniel Sleator和Robert Tarjan
    优点:每次查询会调整树的结构,使被查询频率高的条目更靠近树根。

    注:所有图片来自wiki——http://en.wikipedia.org/wiki/Splay_tree(English)。

    Tree Rotation



     
    树的旋转是splay的基础,对于二叉查找树来说,树的旋转不破坏查找树的结构。
     

    Splaying

     
    Splaying是Splay Tree中的基本操作,为了让被查询的条目更接近树根,Splay Tree使用了树的旋转操作,同时保证二叉排序树的性质不变。
    Splaying的操作受以下三种因素影响:
    • 节点x是父节点p的左孩子还是右孩子
    • 节点p是不是根节点,如果不是
    • 节点p是父节点g的左孩子还是右孩子
    同时有三种基本操作:
     

    Zig Step


    当p为根节点时,进行zip step操作。
    当x是p的左孩子时,对x右旋;
    当x是p的右孩子时,对x左旋。
     

    Zig-Zig Step



    当p不是根节点,且x和p同为左孩子或右孩子时进行Zig-Zig操作。
    当x和p同为左孩子时,依次将p和x右旋;
    当x和p同为右孩子时,依次将p和x左旋。
     
     

    Zig-Zag Step



    当p不是根节点,且x和p不同为左孩子或右孩子时,进行Zig-Zag操作。
    当p为左孩子,x为右孩子时,将x左旋后再右旋。
    当p为右孩子,x为左孩子时,将x右旋后再左旋。
     
    其余操作

    查找操作

     Find(x,S):判断元素x是否在伸展树S表示的有序集中。
    首先,与在二叉查找树中的查找操作一样,在伸展树中查找元素x。如果x在树中,则再执行Splaying(x,S)调整伸展树。

    加入操作

     Insert(x,S):将元素x插入伸展树S表示的有序集中。
    首先,也与处理普通的二叉查找树一样,将x插入到伸展树S中的相应位置上,再执行Splaying(x,S)。

    删除操作

     Delete(x,S):将元素x从伸展树S所表示的有序集中删除。
    首先,用在二叉查找树中查找元素的方法找到x的位置。如果x没有孩子或只有一个孩子,那么直接将x删去,并通过Splaying操作,将x节点的父节点调整
    到伸展树的根节点处。否则,则向下查找x的后继y,用y替代x的位置,最后执行Splaying(y,S),将y调整为伸展树的根。

    合并操作

     join(S1,S2):将两个伸展树S1与S2合并成为一个伸展树。其中S1的所有元素都小于S2的所有元素。首先,我们找到伸展树S1中最大的一个元素x,再通过Splaying(x,S1)将x调整到伸展树S1的根。然后再将S2作为x节点的右子树。这样,就得到了新的伸展树S。

    划分操作

     Split(x,S):以x为界,将伸展树S分离为两棵伸展树S1和S2,其中S1中所有元素都小于x,S2中的所有元素都大于x。首先执行Find(x,S),将元素x调整为伸展树的根节点,则x的左子树就是S1,而右子树为S2。

    其他操作

     除了上面介绍的五种基本操作,伸展树还支持求最大值、求最小值、求前趋、求后继等多种操作,这些基本操作也都是建立在伸展操作的基础上的。
    通常来说,每进行一种操作后都会进行一次Splaying操作,这样可以保证每次操作的平摊时间复杂度是O(logn)。
     
     
    代码
    #include <iostream>
    #include <cstring>
    #include <cstdio>
    #include <string>
    using namespace std;
    #define N 100000
    struct Tree
     {
         int Le,Ri,Fa,s;
     };
    Tree a[N];
    int n=0,Gf=0;
    void RiTn(int x)//右旋
     {
         int i=a[a[x].Fa].Fa;
         if (i==0)
          Gf=i;else
          {
              if (a[i].Le==a[x].Fa)
                a[i].Le=x;else
                a[i].Ri=x;
          }
         if (a[x].Ri!=0)
           a[a[x].Ri].Fa=a[x].Fa;
         a[a[x].Fa].Le=a[x].Ri;
         a[a[x].Fa].Fa=x;
         a[x].Ri=a[x].Fa;
         a[x].Fa=i;
         return;
     }
    void LeTn(int x)//左旋
     {
         int i=a[a[x].Fa].Fa;
         if (i==0)
          Gf=i;else
          {
              if (a[i].Le==a[x].Fa)
                a[i].Le=x;else
                a[i].Ri=x;
          }
         if (a[x].Le!=0)
           a[a[x].Le].Fa=a[x].Fa;
         a[a[x].Fa].Ri=a[x].Le;
         a[a[x].Fa].Fa=x;
         a[x].Le=a[x].Fa;
         a[x].Fa=i;
         return;
     }
    int Find(int x,int y)//查找元素
     {
         if (y==0) 
          return 0;
         if (a[y].s==x) 
          return y;else
           if (a[y].s>x) 
            return Find(x,a[y].Le);else
             return Find(x,a[y].Ri);
     }
    void Insert(int x)//插入操作
     {
         if (a[n].s<a[x].s)
          {
              if (a[x].Le==0)
                {
                    a[x].Le=n;
                    a[n].Fa=x;
               }else 
              Insert(a[x].Le);
          }else
          {
              if (a[x].Ri==0)
                {
                    a[x].Ri=n;
                    a[n].Fa=x;
                }else 
              Insert(a[x].Ri);
          }
         return;
     }
    int Fnext(int x)//查找后继
     {
         if (a[x].Le==0) 
          return x;else
           return Fnext(a[x].Le);
     }
    int main()
     {
         int i,j,k,l,q,w,e,m;
         string b;
         scanf("%d",&m);
         for (i=1;i<=m;i++)
          {
              cin >>b;
              if (b=="Find")
               {
                   scanf("%d",&k);
                l=Find(k,Gf);
                   if (l>0) 
                     {
                     printf("Yes\n");
                        while (Gf!=l) 
                      if (l==a[a[l].Fa].Le)
                        RiTn(l);else
                        LeTn(l);
                  }else
                     printf("No\n");
               }else
              if (b=="Insert")
               {
                   scanf("%d",&k);
                   n++;
                   a[n].s=k;
                a[n].Fa=0;
                a[n].Le=0;
                a[n].Ri=0;
                   if (n==1) 
                  Gf=1;else
                     {
                         Insert(Gf);
                         while (Gf!=n) 
                      if (a[a[n].Fa].Le==n)
                        RiTn(n);else
                        LeTn(n);
                     }
               }else
              if (b=="Delete")
               {
                   scanf("%d",&k);
                   l=Find(k,Gf);
                   if (l==0) continue;
                   if (a[l].Le*a[l].Ri==0)
                    {
                        if (l!=Gf)
                      {
                         if (a[a[l].Fa].Ri==l)
                           a[a[l].Fa].Ri=a[l].Ri+a[l].Le;else
                               a[a[l].Fa].Le=a[l].Ri+a[l].Le;
                         }else 
                         Gf=a[l].Le+a[l].Ri;
                        if (a[l].Le>0)
                          a[a[l].Le].Fa=a[l].Fa;else
                          if (a[l].Ri>0)
                            a[a[l].Ri].Fa=a[l].Fa;
                    a[l].Fa=0;
                    a[l].Le=0;
                    a[l].Ri=0;
                    a[l].s=0;
                    }else 
                    {
                       q=Fnext(a[l].Ri);
                       if (q==a[a[q].Fa].Le)
                      {
                          a[a[q].Fa].Le=a[q].Ri;
                          if (a[q].Ri!=0) 
                          a[a[q].Ri].Fa=a[q].Fa;
                          if (l!=Gf)
                          {
                             if (l==a[a[l].Fa].Ri) 
                               a[a[l].Fa].Ri=q;else
                               a[a[l].Fa].Le=q;
                          }else
                          Gf=q;
                        a[q].Ri=a[l].Ri;
                        a[q].Fa=a[l].Fa;
                        a[q].Le=a[l].Le;
                        if (a[l].Le>0)
                          a[a[l].Le].Fa=q;
                        a[a[l].Ri].Fa=q;
                      }else
                      {
                          if (l!=Gf)
                          {
                             if (l==a[a[l].Fa].Le)
                                 a[a[l].Fa].Le=q;else
                                 a[a[l].Fa].Ri=q;
                          }else
                          Gf=q;
                          a[q].Fa=a[l].Fa;
                          a[q].Le=a[l].Le;
                          if (a[q].Le>0) a[a[q].Le].Fa=q;
                      }
                    a[l].Fa=0;
                    a[l].Le=0;
                    a[l].Ri=0;
                    a[l].s=0;
                    }
               }
          }
         return 0;
     }
     

    应用

     
    Splay Tree可以方便的解决一些区间问题,根据不同形状二叉树先序遍历结果不变的特性,可以将区间按顺序建二叉查找树。
    每次自下而上的一套splay都可以将x移动到根节点的位置,利用这个特性,可以方便的利用Lazy的思想进行区间操作。
    对于每个节点记录size,代表子树中节点的数目,这样就可以很方便地查找区间中的第k小或第k大元素。
    对于一段要处理的区间[x, y],首先splay x-1到root,再splay y+1到root的右孩子,这时root的右孩子的左孩子对应子树就是整个区间。
    这样,大部分区间问题都可以很方便的解决,操作同样也适用于一个或多个条目的添加或删除,和区间的移动。
  • 相关阅读:
    layer ----- 弹层
    php start
    node.js使用iconv-lite和zlib解决gzip压缩和gbk乱码
    AngularJS中promise的使用
    AngularJS中的$routeProvider
    AngularJS入门教程记录
    AngularJS中的$resource
    Javascript原型易错点记录
    触屏相关事件问题记录
    图片预加载
  • 原文地址:https://www.cnblogs.com/HJWJBSR/p/4119476.html
Copyright © 2011-2022 走看看