伸展树

来源:互联网 发布:制作桌面日历软件 编辑:程序博客网 时间:2024/05/16 12:58

一、简介:
伸展树,或者叫自适应查找树,是一种用于保存有序集合的简单高效的数据结构。伸展树实质上是一个二叉查找树。允许查找,插入,删除,删除最小,删除最大,分割,合并等许多操作,这些操作的时间复杂度为O(logN)。由于伸展树可以适应需求序列,因此他们的性能在实际应用中更优秀。
伸展树支持所有的二叉树操作。伸展树不保证最坏情况下的时间复杂度为O(logN)。伸展树的时间复杂度边界是均摊的。尽管一个单独的操作可能很耗时,但对于一个任意的操作序列,时间复杂度可以保证为O(logN)。
二、自调整和均摊分析:
    平衡查找树的一些限制:
1、平衡查找树每个节点都需要保存额外的信息。
2、难于实现,因此插入和删除操作复杂度高,且是潜在的错误点。
3、对于简单的输入,性能并没有什么提高。
    平衡查找树可以考虑提高性能的地方:
1、平衡查找树在最差、平均和最坏情况下的时间复杂度在本质上是相同的。
2、对一个节点的访问,如果第二次访问的时间小于第一次访问,将是非常好的事情。
3、90-10法则。在实际情况中,90%的访问发生在10%的数据上。
4、处理好那90%的情况就很好了。
三、均摊时间边界:
在一颗二叉树中访问一个节点的时间复杂度是这个节点的深度。因此,我们可以重构树的结构,使得被经常访问的节点朝树根的方向移动。尽管这会引入额外的操作,但是经常被访问的节点被移动到了靠近根的位置,因此,对于这部分节点,我们可以很快的访问。根据上面的90-10法则,这样做可以提高性能。
为了达到上面的目的,我们需要使用一种策略──旋转到根(rotate-to-root)。具体实现如下:
旋转分为左旋和右旋,这两个是对称的。图示:
 
为了叙述的方便,上图的右旋叫做X绕Y右旋,左旋叫做Y绕X左旋。
下图展示了将节点3旋转到根:
 
                            图1
首先节点3绕2左旋,然后3绕节点4右旋。
注意:所查找的数据必须符合上面的90-10法则,否则性能上不升反降!!
四、基本的自底向上伸展树:
    应用伸展(splaying)技术,可以得到对数均摊边界的时间复杂度。
    在旋转的时候,可以分为三种情况:
1、zig情况。
    X是查找路径上我们需要旋转的一个非根节点。
    如果X的父节点是根,那么我们用下图所示的方法旋转X到根:
     
                                图2
    这和一个普通的单旋转相同。
2、zig-zag情况。
在这种情况中,X有一个父节点P和祖父节点G(P的父节点)。X是右子节点,P是左子节点,或者反过来。这个就是双旋转。
先是X绕P左旋转,再接着X绕G右旋转。
如图所示:
 
                            图三
3、zig-zig情况。
    这和前一个旋转不同。在这种情况中,X和P都是左子节点或右子节点。
    先是P绕G右旋转,接着X绕P右旋转。
    如图所示:
     
                                    图四
    下面是splay的伪代码:
    P(X) : 获得X的父节点,G(X) : 获得X的祖父节点(=P(P(X)))。
   

 Function Buttom-up-splay:        Do            If X 是 P(X) 的左子结点 Then                If G(X) 为空 Then                    X 绕 P(X)右旋                Else If P(X)是G(X)的左子结点                    P(X) 绕G(X)右旋                    X 绕P(X)右旋                Else                    X绕P(X)右旋                    X绕P(X)左旋 (P(X)和上面一句的不同,是原来的G(X))                Endif            Else If X 是 P(X) 的右子结点 Then                If G(X) 为空 Then                    X 绕 P(X)左旋                Else If P(X)是G(X)的右子结点                    P(X) 绕G(X)左旋                    X 绕P(X)左旋                Else                    X绕P(X)左旋                    X绕P(X)右旋 (P(X)和上面一句的不同,是原来的G(X))                Endif             Endif        While (P(X) != NULL)    EndFunction
    仔细分析zig-zag,可以发现,其实zig-zag就是两次zig。因此上面的代码可以简化:
Function Buttom-up-splay:        Do            If X 是 P(X) 的左子结点 Then                If P(X)是G(X)的左子结点                    P(X) 绕G(X)右旋                Endif                X 绕P(X)右旋            Else If X 是 P(X) 的右子结点 Then                If P(X)是G(X)的右子结点                    P(X) 绕G(X)左旋                Endif                 X 绕P(X)左旋            Endif        While (P(X) != NULL)    EndFunction

    下面是一个例子,旋转节点c到根上。 
 
                                    图五
五、基本伸展树操作:
1、插入:
    当一个节点插入时,伸展操作将执行。因此,新插入的节点在根上。
2、查找:
    如果查找成功(找到),那么由于伸展操作,被查找的节点成为树的新根。
如果查找失败(没有),那么在查找遇到NULL之前的那个节点成为新的根。也就是,如果查找的节点在树中,那么,此时根上的节点就是距离这个节点最近的节点。
3、查找最大最小:
        查找之后执行伸展。
4、删除最大最小:
a)删除最小:
    首先执行查找最小的操作。
这时,要删除的节点就在根上。根据二叉查找树的特点,根没有左子节点。
使用根的右子结点作为新的根,删除旧的包含最小值的根。
b)删除最大:
首先执行查找最大的操作。
删除根,并把被删除的根的左子结点作为新的根。
5、删除:
        将要删除的节点移至根。
        删除根,剩下两个子树L(左子树)和R(右子树)。
        使用DeleteMax查找L的最大节点,此时,L的根没有右子树。
        使R成为L的根的右子树。
        如下图示:
         
                                图六
六、自顶向下的伸展树:
    在自底向上的伸展树中,我们需要求一个节点的父节点和祖父节点,因此这种伸展树难以实现。因此,我们可以构建自顶向下的伸展树。
    当我们沿着树向下搜索某个节点X的时候,我们将搜索路径上的节点及其子树移走。我们构建两棵临时的树──左树和右树。没有被移走的节点构成的树称作中树。在伸展操作的过程中:
1、当前节点X是中树的根。
2、左树L保存小于X的节点。
3、右树R保存大于X的节点。
开始时候,X是树T的根,左右树L和R都是空的。和前面的自下而上相同,自上而下也分三种情况:
1、zig:
 
                                图七
    如上图,在搜索到X的时候,所查找的节点比X小,将Y旋转到中树的树根。旋转之后,X及其右子树被移动到右树上。很显然,右树上的节点都大于所要查找的节点。注意X被放置在右树的最小的位置,也就是X及其子树比原先的右树中所有的节点都要小。这是由于越是在路径前面被移动到右树的节点,其值越大。读者可以分析一下树的结构,原因很简单。
2、zig-zig:
 
                                图八
    在这种情况下,所查找的节点在Z的子树中,也就是,所查找的节点比X和Y都小。所以要将X,Y及其右子树都移动到右树中。首先是Y绕X右旋,然后Z绕Y右旋,最后将Z的右子树(此时Z的右子节点为Y)移动到右树中。注意右树中挂载点的位置。
3、zig-zag:
 
                            图九
    在这种情况中,首先将Y右旋到根。这和Zig的情况是一样的。然后变成上图右边所示的形状。接着,对Z进行左旋,将Y及其左子树移动到左树上。这样,这种情况就被分成了两个Zig情况。这样,在编程的时候就会简化,但是操作的数目增加(相当于两次Zig情况)。
    最后,在查找到节点后,将三棵树合并。如图:
 
                                图十
    将中树的左右子树分别连接到左树的右子树和右树的左子树上。将左右树作为X的左右子树。重新最成了一所查找的节点为根的树。
    下面给出伪代码:
    右连接:将当前根及其右子树连接到右树上。左子结点作为新根。
    左连接:将当前根及其左子树连接到左树上。右子结点作为新根。
    T : 当前的根节点。
Function Top-Down-Splay      Do           If X 小于 T Then                If X 等于 T 的左子结点 Then                   右连接                ElseIf X 小于 T 的左子结点 Then                  T的左子节点绕T右旋                  右连接                Else X大于 T 的左子结点 Then                  右连接                  左连接                EndIf              ElseIf X大于 T Then                IF X 等于 T 的右子结点 Then                  左连接                ElseIf X 大于 T 的右子结点 Then                  T的右子节点绕T左旋                  左连接                Else X小于 T 的右子结点‘ Then                  左连接                  右连接                EndIf           EndIf      While  !(找到 X或遇到空节点)       组合左中右树 EndFunction
同样,上面的三种情况也可以简化:

Function Top-Down-Splay        Do               If X 小于 T Then                    If X 小于 T 的左孩子 Then                      T的左子节点绕T右旋                    EndIf                    右连接               Else If X大于 T Then                    If X 大于 T 的右孩子 Then                      T的右子节点绕T左旋                   EndIf 左连接          EndIf While  !(找到 X或遇到空节点) 组合左中右树     EndFuntion

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

 

 

复制代码
1 /*
2 An implementation of top-down splaying
3 D. Sleator <sleator@cs.cmu.edu>
4 March 1992
5  */
6 #include <stdlib.h>
7 #include <stdio.h>
8  int size; /* number of nodes in the tree */
9 /* Not actually needed for any of the operations */
10 typedef struct tree_node Tree;
11  struct tree_node
12 {
13 Tree * left, * right;
14 int item;
15 };
16
17 Tree * splay (int i, Tree * t)
18 {
19  /* Simple top down splay, not requiring i to be in the tree t. */
20  /* What it does is described above. */
21 Tree N, *l, *r, *y;
22 if (t == NULL)
23 return t;
24 N.left = N.right = NULL;
25 l = r = &N;
26 for (;;)
27 {
28 if (i < t->item)
29 {
30 if (t->left == NULL)
31 {
32 break;
33 }
34 if (i < t->left->item)
35 {
36 y = t->left; /* rotate right */
37 t->left = y->right;
38 y->right = t;
39 t = y;
40 if (t->left == NULL)
41 {
42 break;
43 }
44 }
45 r->left = t; /* link right */
46 r = t;
47 t = t->left;
48 }
49 else if (i > t->item)
50 {
51 if (t->right == NULL)
52 {
53 break;
54 }
55 if (i > t->right->item)
56 {
57 y = t->right; /* rotate left */
58 t->right = y->left;
59 y->left = t;
60 t = y;
61 if (t->right == NULL)
62 {
63 break;
64 }
65 }
66 l->right = t; /* link left */
67 l = t;
68 t = t->right;
69 }
70 else
71 {
72 break;
73 }
74 }
75 l->right = t->left; /* assemble */
76 r->left = t->right;
77 t->left = N.right;
78 t->right = N.left;
79 return t;
80 }
81  /* Here is how sedgewick would have written this. */
82 /* It does the same thing. */
83 Tree * sedgewickized_splay (int i, Tree * t)
84 {
85 Tree N, *l, *r, *y;
86 if (t == NULL)
87 {
88 return t;
89 }
90 N.left = N.right = NULL;
91 l = r = &N;
92 for (;;)
93 {
94 if (i < t->item)
95 {
96 if (t->left != NULL && i < t->left->item)
97 {
98 y = t->left;
99 t->left = y->right;
100 y->right = t;
101 t = y;
102 }
103 if (t->left == NULL)
104 {
105 break;
106 }
107 r->left = t;
108 r = t;
109 t = t->left;
110 }
111 else if (i > t->item)
112 {
113 if (t->right != NULL && i > t->right->item)
114 {
115 y = t->right;
116 t->right = y->left;
117 y->left = t;
118 t = y;
119 }
120 if (t->right == NULL)
121 {
122 break;
123 }
124 l->right = t;
125 l = t;
126 t = t->right;
127 }
128 else
129 {
130 break;
131 }
132 }
133 l->right=t->left;
134 r->left=t->right;
135 t->left=N.right;
136 t->right=N.left;
137 return t;
138 }
139
140 Tree * insert(int i, Tree * t)
141 {
142 /* Insert i into the tree t, unless it's already there. */
143 /* Return a pointer to the resulting tree. */
144 Tree * new;
145
146 new = (Tree *) malloc (sizeof (Tree));
147 if (new == NULL)
148 {
149 printf("Ran out of space\n");
150 exit(1);
151 }
152 new->item = i;
153 if (t == NULL)
154 {
155 new->left = new->right = NULL;
156 size = 1;
157 return new;
158 }
159 t = splay(i,t);
160 if (i < t->item)
161 {
162 new->left = t->left;
163 new->right = t;
164 t->left = NULL;
165 size ++;
166 return new;
167 }
168 else if (i > t->item)
169 {
170 new->right = t->right;
171 new->left = t;
172 t->right = NULL;
173 size++;
174 return new;
175 }
176 else
177 {
178 /* We get here if it's already in the tree */
179 /* Don't add it again */
180 free(new);
181 return t;
182 }
183 }
184
185 Tree * delete(int i, Tree * t)
186 {
187 /* Deletes i from the tree if it's there. */
188 /* Return a pointer to the resulting tree. */
189 Tree * x;
190 if (t==NULL)
191 {
192 return NULL;
193 }
194 t = splay(i,t);
195 if (i == t->item)
196 { /* found it */
197 if (t->left == NULL)
198 {
199 x = t->right;
200 }
201 else
202 {
203 x = splay(i, t->left);
204 x->right = t->right;
205 }
206 size--;
207 free(t);
208 return x;
209 }
210 return t; /* It wasn't there */
211 }
212
213 int main(int argv, char *argc[])
214 {
215 /* A sample use of these functions. Start with the empty tree, */
216 /* insert some stuff into it, and then delete it */
217 Tree * root;
218 int i;
219 root = NULL; /* the empty tree */
220 size = 0;
221 for (i = 0; i < 1024; i++)
222 {
223 root = insert((541*i) & (1023), root);
224 }
225 printf("size = %d\n", size);
226 for (i = 0; i < 1024; i++)
227 {
228 root = delete((541*i) & (1023), root);
229 }
230 printf("size = %d\n", size);
231 }
232
233
复制代码

PS:文章的参考来源以丢失。。。

另一份实现代码

#include<stdio.h>#include<malloc.h>#include<stdlib.h>struct node {        int data;        struct node *parent;        struct node *left;        struct node *right;};int data_print(struct node *x);struct node *rightrotation(struct node *p,struct node *root);struct node *leftrotation(struct node *p,struct node *root);void splay (struct node *x, struct node *root);struct node *insert(struct node *p,int value);struct node *inorder(struct node *p);struct node *delete(struct node *p,int value);struct node *successor(struct node *x);struct node *lookup(struct node *p,int value); void splay (struct node *x, struct node *root){        struct node *p,*g;        /*check if node x is the root node*/        if(x==root)                return;        /*Performs Zig step*/        else if(x->parent==root)        {                if(x==x->parent->left)                        root=rightrotation(root,root);                else                        root=leftrotation(root,root);        }        else        {                p=x->parent; /*now points to parent of x*/                g=p->parent; /*now points to parent of x's parent*/                /*Performs the Zig-zig step when x is left and x's parent is left*/                if(x==p->left&&p==g->left)                {                        root=rightrotation(g,root);                        root=rightrotation(p,root);                }                /*Performs the Zig-zig step when x is right and x's parent is right*/                else if(x==p->right&&p==g->right)                {                        root=leftrotation(g,root);                        root=leftrotation(p,root);                }                /*Performs the Zig-zag step when x's is right and x's parent is left*/                else if(x==p->right&&p==g->left)                {                        root=leftrotation(p,root);                        root=rightrotation(g,root);                }                /*Performs the Zig-zag step when x's is left and x's parent is right*/                else if(x==p->left&&p==g->right)                {                        root=rightrotation(p,root);                        root=leftrotation(g,root);                }                splay(x, root);        }}struct node *rightrotation(struct node *p,struct node *root){        struct node *x;        x = p->left;        p->left = x->right;        if (x->right!=NULL) x->right->parent = p;        x->right = p;        if (p->parent!=NULL)                if(p==p->parent->right) p->parent->right=x;                else                         p->parent->left=x;        x->parent = p->parent;        p->parent = x;        if (p==root)                return x;        else                 return root;}struct node *leftrotation(struct node *p,struct node *root){        struct node *x;        x = p->right;        p->right = x->left;        if (x->left!=NULL) x->left->parent = p;        x->left = p;        if (p->parent!=NULL)                if (p==p->parent->left) p->parent->left=x;                else                         p->parent->right=x;        x->parent = p->parent;        p->parent = x;        if(p==root)                 return x;        else                return root;}struct node *insert(struct node *p,int value){        struct node *temp1,*temp2,*par,*x;        if(p == NULL)        {                p=(struct node *)malloc(sizeof(struct node));                if(p != NULL)                {                        p->data = value;                        p->parent = NULL;                        p->left = NULL;                        p->right = NULL;                }                else                {                        printf("No memory is allocated\n");                        exit(0);                }                return(p);        } //the case 2 says that we must splay newly inserted node to root        else        {                        temp2 = p;                        while(temp2 != NULL)                        {                                temp1 = temp2;                                if(temp2->data > value)                                        temp2 = temp2->left;                                else if(temp2->data < value)                                        temp2 = temp2->right;                                else                                        if(temp2->data == value)                                                return temp2;                        }                        if(temp1->data > value)                        {                                par = temp1;//temp1 having the parent address,so that's it                                 temp1->left = (struct node *)malloc(sizeof(struct node));                                temp1= temp1->left;                                if(temp1 != NULL)                                {                                        temp1->data = value;                                        temp1->parent = par;//store the parent address.                                        temp1->left = NULL;                                        temp1->right = NULL;                                }                                else                                {                                        printf("No memory is allocated\n");                                        exit(0);                                }                        }                        else                        {                                par = temp1;//temp1 having the parent node address.                                temp1->right = (struct node *)malloc(sizeof(struct node));                                temp1 = temp1->right;                                if(temp1 != NULL)                                {                                        temp1->data = value;                                        temp1->parent = par;//store the parent address                                        temp1->left = NULL;                                        temp1->right = NULL;                                }                                else                                {                                        printf("No memory is allocated\n");                                        exit(0);                                }                        }        }        splay(temp1,p);//temp1 will be new root after splaying        return (temp1);}struct node *inorder(struct node *p){        if(p != NULL)        {                inorder(p->left);                printf("CURRENT %d\t",p->data);                printf("LEFT %d\t",data_print(p->left));                printf("PARENT %d\t",data_print(p->parent));                printf("RIGHT %d\t\n",data_print(p->right));                inorder(p->right);        }}struct node *delete(struct node *p,int value){        struct node *x,*y,*p1;        struct node *root;        struct node *s;        root = p;        x = lookup(p,value);        if(x->data == value)        {       //if the deleted element is leaf                if((x->left == NULL) && (x->right == NULL))                {                        y = x->parent;                        if(x ==(x->parent->right))                                 y->right = NULL;                        else                                 y->left = NULL;                        free(x);                }                //if deleted element having left child only                else if((x->left != NULL) &&(x->right == NULL))                {                        if(x == (x->parent->left))                        {                                y = x->parent;                                x->left->parent = y;                                y->left = x->left;                                free(x);                        }                        else                        {                                y = x->parent;                                x->left->parent = y;                                y->right = x->left;                                free(x);                        }                }                //if deleted element having right child only                else if((x->left == NULL) && (x->right != NULL))                {                        if(x == (x->parent->left))                        {                                y = x->parent;                                x->right->parent = y;                                y->left = x->right;                                free(x);                        }                        else                        {                                y = x->parent;                                x->right->parent = y;                                y->right = x->right;                                free(x);                        }                }                //if the deleted element having two children                else if((x->left != NULL) && (x->right != NULL))                {                        if(x == (x->parent->left))                        {                                s = successor(x);                                if(s != x->right)                                {                                        y = s->parent;                                        if(s->right != NULL)                                        {                                                s->right->parent = y;                                                y->left = s->right;                                        }                                        else y->left = NULL;                                        s->parent = x->parent;                                        x->right->parent = s;                                        x->left->parent = s;                                        s->right = x->right;                                        s->left = x->left;                                        x->parent->left = s;                                }                                else                                {                                        y = s;                                        s->parent = x->parent;                                        x->left->parent = s;                                        s->left = x->left;                                        x->parent->left = s;                                }                                free(x);                        }                        else if(x == (x->parent->right))                        {                                s = successor(x);                                if(s != x->right)                                {                                        y = s->parent;                                        if(s->right != NULL)                                        {                                                s->right->parent = y;                                                y->left = s->right;                                        }                                        else y->left = NULL;                                        s->parent = x->parent;                                        x->right->parent = s;                                        x->left->parent = s;                                        s->right = x->right;                                        s->left = x->left;                                        x->parent->right = s;                                }                                else                                {                                        y = s;                                        s->parent = x->parent;                                        x->left->parent = s;                                        s->left = x->left;                                        x->parent->right = s;                                }                                free(x);                        }                 }                splay(y,root);        }        else        {                splay(x,root);        }}struct node *successor(struct node *x){        struct node *temp,*temp2;        temp=temp2=x->right;        while(temp != NULL)        {                temp2 = temp;                temp = temp->left;        }        return temp2;}//p is a root element of the treestruct node *lookup(struct node *p,int value){        struct node *temp1,*temp2;        if(p != NULL)        {                temp1 = p;                while(temp1 != NULL)                {                        temp2 = temp1;                        if(temp1->data > value)                                temp1 = temp1->left;                        else if(temp1->data < value)                                temp1 = temp1->right;                        else                                        return temp1;                }                return temp2;        }        else        {                printf("NO element in the tree\n");                exit(0);        }}struct node *search(struct node *p,int value){        struct node *x,*root;        root = p;        x = lookup(p,value);        if(x->data == value)        {                printf("Inside search if\n");                splay(x,root);        }        else        {                printf("Inside search else\n");                splay(x,root);        }}main(){        struct node *root;//the root element        struct node *x;//x is which element will come to root.        int i;        root = NULL;        int choice = 0;        int ele;        while(1)        {                printf("\n\n 1.Insert");                printf("\n\n 2.Delete");                printf("\n\n 3.Search");                printf("\n\n 4.Display\n");                printf("\n\n Enter your choice:");                scanf("%d",&choice);                if(choice==5)                        exit(0);                switch(choice)                {                        case 1:                                printf("\n\n Enter the element to be inserted:");                                scanf("%d",&ele);                                x = insert(root,ele);                                if(root != NULL)                                {                                        splay(x,root);                                }                                root = x;                                break;                        case 2:                                if(root == NULL)                                {                                        printf("\n Empty tree...");                                        continue;                                }                                printf("\n\n Enter the element to be delete:");                                scanf("%d",&ele);                                root = delete(root,ele);                                break;                        case 3:                                printf("Enter the element to be search\n");                                scanf("%d",&ele);                                x = lookup(root,ele);                                        splay(x,root);                                root = x;                                break;                        case 4:                                printf("The elements are\n");                                inorder(root);                                break;                        default:                                printf("Wrong choice\n");                                break;                }        }}int data_print(struct node *x){        if ( x==NULL )                return 0;        else                return x->data;}/*some suggestion this code is not fully functional for example if you have inserted some elements then try to delete root then it may not work because we are calling right and left child of a null value(parent of root) which is not allowed and will give segmentation fault Also for inserting second element because of splaying twice(once in insert and one in main) will give error So I have made those changes but mainly in my cpp( c plus plus file) file, but I guess wiki will itself look into this and made  these changes */


0 0
原创粉丝点击