zoukankan      html  css  js  c++  java
  • 伸展树的点点滴滴

    伸展树(Splay Tree)是AVL树不错的替代,它有以下几个特点:
    (1)它是二叉查找树的改进,所以具有二叉查找树的有序性。
    (2)对伸展树的操作的平摊复杂度是O(log2n)。
    (3)伸展树的空间要求、编程难度非常低。

    提到伸展树,就不得不提到AVL树和Read-Black树,虽然这两种树能够保证各种操作在最坏情况下都为logN,但是两都实现都比较复杂。而在实际情况中,90%的访问发生在10%的数据上。因此,我们可以重构树的结构,使得被经常访问的节点朝树根的方向移动。尽管这会引入额外的操作,但是经常被访问的节点被移动到了靠近根的位置,因此,对于这部分节点,我们可以很快的访问。这样,就能使得平摊复杂度为logN。

    1、自底向上的伸展树
    伸展操作Splay(x,S)是在保持伸展树有序性的前提下,通过一系列旋转操作将伸展树S中的元素x调整至树的根部的操作。
    在旋转的过程中,要分三种情况分别处理:
    (1)Zig 或 Zag
    (2)Zig-Zig 或 Zag-Zag
    (3)Zig-Zag 或 Zag-Zig
    1.1、Zig或Zag操作
    节点x的父节点y是根节点。

    1.2、Zig-Zig或Zag-Zag操作
    节点x的父节点y不是根节点,且x与y同时是各自父节点的左孩子或者同时是各自父节点的右孩子。

    1.3、Zig-Zag或Zag-Zig操作
    节点x的父节点y不是根节点,x与y中一个是其父节点的左孩子而另一个是其父节点的右孩子。

    2、自顶向下的伸展树
        在自底向上的伸展树中,我们需要求一个节点的父节点和祖父节点,因此这种伸展树难以实现。因此,我们可以构建自顶向下的伸展树。
        当我们沿着树向下搜索某个节点X的时候,我们将搜索路径上的节点及其子树移走。我们构建两棵临时的树──左树和右树。没有被移走的节点构成的树称作中树。在伸展操作的过程中:
    (1)当前节点X是中树的根。
    (2)左树L保存小于X的节点。
    (3)右树R保存大于X的节点。
    开始时候,X是树T的根,左右树L和R都是空的。和前面的自下而上相同,自上而下也分三种情况:
    2.1、Zig操作

    如上图,在搜索到X的时候,所查找的节点比X小,将Y旋转到中树的树根。旋转之后,X及其右子树被移动到右树上。很显然,右树上的节点都大于所要查找的节点。注意X被放置在右树的最小的位置,也就是X及其子树比原先的右树中所有的节点都要小。这是由于越是在路径前面被移动到右树的节点,其值越大。

    2.2、Zig-Zig操作

    这种情况下,所查找的节点在Z的子树中,也就是,所查找的节点比X和Y都小。所以要将X,Y及其右子树都移动到右树中。首先是Y绕X右旋,然后Z绕Y右旋,最后将Z的右子树(此时Z的右子节点为Y)移动到右树中。

    2.3、Zig-Zag操作

    这种情况中,首先将Y右旋到根。这和Zig的情况是一样的,然后变成上图右边所示的形状。此时,就与Zag(与Zig相反)的情况一样了。

    最后,在查找到节点后,将三棵树合并。如图:


    2.4、示例:
    下面是一个查找节点19的例子。在例子中,树中并没有节点19,最后,距离节点最近的节点18被旋转到了根作为新的根。节点20也是距离节点19最近的节点,但是节点20没有成为新根,这和节点20在原来树中的位置有关系。

    3、实现
    3.1、splay操作

    代码
    tree_node * splay (int i, tree_node * t) {
        tree_node N, 
    *l, *r, *y;
        
    if (t == NULL) 
            
    return t;
        N.left 
    = N.right = NULL;
        l 
    = r = &N;

        
    for (;;)
        {
            
    if (i < t->item) 
            {
                
    if (t->left == NULL) 
                    
    break;
                
    if (i < t->left->item) 
                {
                    y 
    = t->left;                           /* rotate right */
                    t
    ->left = y->right;
                    y
    ->right = t;
                    t 
    = y;
                    
    if (t->left == NULL) 
                        
    break;
                }
                r
    ->left = t;                               /* link right */
                r 
    = t;
                t 
    = t->left;
            } 
    else if (i > t->item)
            {
                
    if (t->right == NULL) 
                    
    break;
                
    if (i > t->right->item) 
                {
                    y 
    = t->right;                          /* rotate left */
                    t
    ->right = y->left;
                    y
    ->left = t;
                    t 
    = y;
                    
    if (t->right == NULL) 
                        
    break;
                }
                l
    ->right = t;                              /* link left */
                l 
    = t;
                t 
    = t->right;
            } 
    else {
                
    break;
            }
        }
        l
    ->right = t->left;                                /* assemble */
        r
    ->left = t->right;
        t
    ->left = N.right;
        t
    ->right = N.left;
        
    return t;
    }

    Rotate right(查找10):

    Link right:

    Assemble:

    Rotate left(查找20):

    Link left:

    3.2、插入操作

    代码
     1 /*
     2 **将i插入树t中,返回树的根结点(item值==i)
     3 */
     4 tree_node* ST_insert(int i, tree_node *t) {
     5     /* Insert i into the tree t, unless it's already there.    */
     6     /* Return a pointer to the resulting tree.                 */
     7     tree_node* node;
     8     
     9     node = (tree_node *) malloc (sizeof (tree_node));
    10     if (node == NULL){
    11         printf("Ran out of space\n");
    12         exit(1);
    13     }
    14     node->item = i;
    15     if (t == NULL) {
    16         node->left = node->right = NULL;
    17         size = 1;
    18         return node;
    19     }
    20     t = splay(i,t);
    21     if (i < t->item) {  //令t为i的右子树
    22         node->left = t->left;
    23         node->right = t;
    24         t->left = NULL;
    25         size ++;
    26         return node;
    27     } else if (i > t->item) { //令t为i的左子树
    28         node->right = t->right;
    29         node->left = t;
    30         t->right = NULL;
    31         size++;
    32         return node;
    33     } else { 
    34         free(node); //i值已经存在于树t中
    35         return t;
    36     }
    37 }

    3.3、删除操作

    代码
     1 /*
     2 **从树中删除i,返回树的根结点
     3 */
     4 tree_node* ST_delete(int i, tree_node* t) {
     5     /* Deletes i from the tree if it's there.               */
     6     /* Return a pointer to the resulting tree.              */
     7     tree_node* x;
     8     if (t==NULL) 
     9         return NULL;
    10     t = splay(i,t);
    11     if (i == t->item) {               /* found it */
    12         if (t->left == NULL) { //左子树为空,则x指向右子树即可
    13             x = t->right;
    14         } else {
    15             x = splay(i, t->left); //查找左子树中最大结点max,令右子树为max的右子树
    16             x->right = t->right;
    17         }
    18         size--;
    19         free(t);
    20         return x;
    21     }
    22     return t;                         /* It wasn't there */
    23 }

    完整代码:

    代码
      1 #include <stdio.h>
      2 #include <stdlib.h>
      3 
      4 int     size; //结点数量
      5 
      6 #define        NUM        20
      7 
      8 typedef struct tree_node{
      9     struct tree_node*    left;
     10     struct tree_node*    right;
     11     int        item;
     12 }tree_node;
     13 
     14 tree_node* splay (int i, tree_node* t) {
     15     tree_node N, *l, *r, *y;
     16 
     17     if (t == NULL) 
     18         return t;
     19 
     20     N.left = N.right = NULL;
     21     l = r = &N;
     22 
     23     for (;;)
     24     {
     25         if (i < t->item) 
     26         {
     27             if (t->left == NULL) 
     28                 break;
     29             if (i < t->left->item) 
     30             {
     31                 y = t->left;                           /* rotate right */
     32                 t->left = y->right;
     33                 y->right = t;
     34                 t = y;
     35                 if (t->left == NULL) 
     36                     break;
     37             }
     38             r->left = t;                               /* link right */
     39             r = t;
     40             t = t->left;
     41         } else if (i > t->item)
     42         {
     43             if (t->right == NULL) 
     44                 break;
     45             if (i > t->right->item) 
     46             {
     47                 y = t->right;                          /* rotate left */
     48                 t->right = y->left;
     49                 y->left = t;
     50                 t = y;
     51                 if (t->right == NULL) 
     52                     break;
     53             }
     54             l->right = t;                              /* link left */
     55             l = t;
     56             t = t->right;
     57         } else {
     58             break;
     59         }
     60     }
     61     l->right = t->left;                                /* assemble */
     62     r->left = t->right;
     63     t->left = N.right;
     64     t->right = N.left;
     65     return t;
     66 }
     67 
     68 /*
     69 **将i插入树t中,返回树的根结点(item值==i)
     70 */
     71 tree_node* ST_insert(int i, tree_node *t) {
     72     /* Insert i into the tree t, unless it's already there.    */
     73     /* Return a pointer to the resulting tree.                 */
     74     tree_node* node;
     75     
     76     node = (tree_node *) malloc (sizeof (tree_node));
     77     if (node == NULL){
     78         printf("Ran out of space\n");
     79         exit(1);
     80     }
     81     node->item = i;
     82     if (t == NULL) {
     83         node->left = node->right = NULL;
     84         size = 1;
     85         return node;
     86     }
     87     t = splay(i,t);
     88     if (i < t->item) {  //令t为i的右子树
     89         node->left = t->left;
     90         node->right = t;
     91         t->left = NULL;
     92         size ++;
     93         return node;
     94     } else if (i > t->item) { //令t为i的左子树
     95         node->right = t->right;
     96         node->left = t;
     97         t->right = NULL;
     98         size++;
     99         return node;
    100     } else { 
    101         free(node); //i值已经存在于树t中
    102         return t;
    103     }
    104 }
    105 
    106 
    107 /*
    108 **从树中删除i,返回树的根结点
    109 */
    110 tree_node* ST_delete(int i, tree_node* t) {
    111     /* Deletes i from the tree if it's there.               */
    112     /* Return a pointer to the resulting tree.              */
    113     tree_node* x;
    114     if (t==NULL) 
    115         return NULL;
    116     t = splay(i,t);
    117     if (i == t->item) {               /* found it */
    118         if (t->left == NULL) { //左子树为空,则x指向右子树即可
    119             x = t->right;
    120         } else {
    121             x = splay(i, t->left); //查找左子树中最大结点max,令右子树为max的右子树
    122             x->right = t->right;
    123         }
    124         size--;
    125         free(t);
    126         return x;
    127     }
    128     return t;                         /* It wasn't there */
    129 }
    130 
    131 void ST_inoder_traverse(tree_node*    node)
    132 {
    133     if(node != NULL)
    134     {
    135         ST_inoder_traverse(node->left);
    136         printf("%d ", node->item);
    137         ST_inoder_traverse(node->right);
    138     }
    139 }
    140 
    141 void ST_pre_traverse(tree_node*    node)
    142 {
    143     if(node != NULL)
    144     {
    145         printf("%d ", node->item);
    146         ST_pre_traverse(node->left);
    147         ST_pre_traverse(node->right);
    148     }
    149 }
    150 
    151 
    152 void main() {
    153     /* A sample use of these functions.  Start with the empty tree,         */
    154     /* insert some stuff into it, and then delete it                        */
    155     tree_node* root;
    156     int i;
    157 
    158     root = NULL;              /* the empty tree */
    159     size = 0;
    160 
    161     for(i = 0; i < NUM; i++)
    162         root = ST_insert(rand()%NUM, root);
    163 
    164     ST_pre_traverse(root);
    165     printf("\n");
    166     ST_inoder_traverse(root);
    167 
    168     for(i = 0; i < NUM; i++)
    169         root = ST_delete(i, root);
    170 
    171     printf("\nsize = %d\n", size);
    172 }
    173 
  • 相关阅读:
    USACO 3.3 A Game
    USACO 3.3 Camelot
    USACO 3.3 Shopping Offers
    USACO 3.3 TEXT Eulerian Tour中的Cows on Parade一点理解
    USACO 3.3 Riding the Fences
    USACO 3.2 Magic Squares
    USACO 3.2 Stringsobits
    USACO 3.2 Factorials
    USACO 3.2 Contact
    USACO 3.1 Humble Numbers
  • 原文地址:https://www.cnblogs.com/hustcat/p/1742012.html
Copyright © 2011-2022 走看看