伸展树的点点滴滴

伸展树(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 

 

 

posted @ 2010-05-23 13:12  YY哥  阅读(2138)  评论(2编辑  收藏  举报