伸展树的点点滴滴
伸展树(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 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、插入操作
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、删除操作
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 }
完整代码:
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