• 伸展树的点点滴滴


    伸展树(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 
  • 相关阅读:
    和为S的连续正数序列
    数组中的逆序对
    剑指offer:数组中出现次数超过一半的数字
    剑指offer12:矩阵中的路径
    剑指offer(62):孩子们的游戏(圆圈中最后剩下的数)约瑟夫环问题
    剑指offer41:数据流中的中位数
    剑指offer56:数组中只出现一次的数字
    不用加减乘除做加法
    输入一个递增排序的数组和一个数字s,在数组中查找两个数,使得它们的和正好是s。如果有多对数字的和等于s,输出任意一对即可
    求树的高度
  • 原文地址:https://www.cnblogs.com/hustcat/p/1742012.html
Copyright © 2020-2023  润新知